@prave/shared 1.3.0 → 1.4.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.
@@ -1,3 +1,4 @@
1
1
  export * from './classify.js';
2
2
  export * from './compile-skill.js';
3
+ export * from './secret-scanner.js';
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAA;AAC7B,cAAc,oBAAoB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAA;AAC7B,cAAc,oBAAoB,CAAA;AAClC,cAAc,qBAAqB,CAAA"}
package/dist/lib/index.js CHANGED
@@ -1,2 +1,3 @@
1
1
  export * from './classify.js';
2
2
  export * from './compile-skill.js';
3
+ export * from './secret-scanner.js';
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Secret-scanner — shared pre-insert gate for skill bundles.
3
+ *
4
+ * Two layers:
5
+ *
6
+ * 1. **Path patterns** — file names / paths that are categorically
7
+ * forbidden in a public-facing skill bundle. Hits are an immediate
8
+ * reject, no content peek needed.
9
+ *
10
+ * 2. **Content regexes** — high-confidence credential shapes that
11
+ * stand out even inside a text body. We only run these against
12
+ * files that look like text (we don't want to false-match a key
13
+ * shape inside a compressed binary blob).
14
+ *
15
+ * The scanner is called from two places:
16
+ *
17
+ * • `apps/worker/src/jobs/scan-github-repo.job.ts` — silent reject,
18
+ * bundle is marked 'rejected' and never surfaces in the catalogue.
19
+ * • `apps/api/src/services/bundle-storage.service.ts` — surfaces the
20
+ * finding back to the user so they can scrub the file and re-deploy.
21
+ *
22
+ * Same code, two failure modes. Keep this file dependency-free so the
23
+ * worker (Node) and the API (Node) and a future web-side validator
24
+ * (browser) can all run it without bundling friction.
25
+ */
26
+ export interface SecretFinding {
27
+ /** Path of the offending file within the bundle. */
28
+ path: string;
29
+ /** Short identifier of the rule that fired (`env-file`, `aws-key`, …). */
30
+ rule: string;
31
+ /**
32
+ * 1-indexed line where the match was found, when a content regex
33
+ * fired. Omitted for path-based rejects.
34
+ */
35
+ line?: number;
36
+ }
37
+ export interface SecretScanInput {
38
+ /** File path relative to the bundle root, e.g. `scripts/post.ts`. */
39
+ path: string;
40
+ /** File content as a UTF-8 string. NULL/undefined for binary files. */
41
+ content?: string | null;
42
+ }
43
+ export interface SecretScanResult {
44
+ /** Final verdict. `clean` means the bundle is safe to expose / run. */
45
+ status: 'clean' | 'rejected';
46
+ /** Every triggered rule. Empty when `status === 'clean'`. */
47
+ findings: SecretFinding[];
48
+ }
49
+ /**
50
+ * Run both layers across a list of files. Returns a verdict + every
51
+ * matching rule across every file. Empty findings = clean bundle.
52
+ *
53
+ * Files where `content` is missing skip the content-regex stage but
54
+ * still trip path rules.
55
+ */
56
+ export declare function scanForSecrets(files: SecretScanInput[]): SecretScanResult;
57
+ /**
58
+ * Whether a path likely points at a text-readable file (worth running
59
+ * content regexes against). Used by callers to filter the input list
60
+ * before they bother loading binary blobs.
61
+ */
62
+ export declare function isLikelyTextPath(path: string): boolean;
63
+ //# sourceMappingURL=secret-scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret-scanner.d.ts","sourceRoot":"","sources":["../../src/lib/secret-scanner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,MAAM,WAAW,aAAa;IAC5B,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAA;IACZ,0EAA0E;IAC1E,IAAI,EAAE,MAAM,CAAA;IACZ;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,eAAe;IAC9B,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAA;IACZ,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,uEAAuE;IACvE,MAAM,EAAE,OAAO,GAAG,UAAU,CAAA;IAC5B,6DAA6D;IAC7D,QAAQ,EAAE,aAAa,EAAE,CAAA;CAC1B;AAwGD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,gBAAgB,CAmCzE;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAStD"}
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Secret-scanner — shared pre-insert gate for skill bundles.
3
+ *
4
+ * Two layers:
5
+ *
6
+ * 1. **Path patterns** — file names / paths that are categorically
7
+ * forbidden in a public-facing skill bundle. Hits are an immediate
8
+ * reject, no content peek needed.
9
+ *
10
+ * 2. **Content regexes** — high-confidence credential shapes that
11
+ * stand out even inside a text body. We only run these against
12
+ * files that look like text (we don't want to false-match a key
13
+ * shape inside a compressed binary blob).
14
+ *
15
+ * The scanner is called from two places:
16
+ *
17
+ * • `apps/worker/src/jobs/scan-github-repo.job.ts` — silent reject,
18
+ * bundle is marked 'rejected' and never surfaces in the catalogue.
19
+ * • `apps/api/src/services/bundle-storage.service.ts` — surfaces the
20
+ * finding back to the user so they can scrub the file and re-deploy.
21
+ *
22
+ * Same code, two failure modes. Keep this file dependency-free so the
23
+ * worker (Node) and the API (Node) and a future web-side validator
24
+ * (browser) can all run it without bundling friction.
25
+ */
26
+ // ── Path patterns ────────────────────────────────────────────────────
27
+ // Matched against the lowercased file path. Anchored at the basename
28
+ // where it makes sense (`.env`) and as substring elsewhere (`secrets/`).
29
+ const PATH_RULES = [
30
+ {
31
+ rule: 'env-file',
32
+ test: (p) => {
33
+ const base = basename(p);
34
+ // .env, .env.local, .env.production, .envrc — but NOT .env.example
35
+ // or .env.sample (templates are encouraged).
36
+ if (base === '.env' || base.startsWith('.env.')) {
37
+ return !/\.(example|sample|template)$/.test(base);
38
+ }
39
+ if (base === '.envrc')
40
+ return true;
41
+ return false;
42
+ },
43
+ },
44
+ {
45
+ rule: 'private-key',
46
+ test: (p) => /\.(pem|p12|pfx|key|jks)$/i.test(p),
47
+ },
48
+ {
49
+ rule: 'ssh-private-key',
50
+ test: (p) => {
51
+ const base = basename(p);
52
+ return /^id_(rsa|ecdsa|ed25519|dsa)$/.test(base);
53
+ },
54
+ },
55
+ {
56
+ rule: 'gcp-service-account',
57
+ test: (p) => {
58
+ const base = basename(p).toLowerCase();
59
+ return (base === 'service-account.json' ||
60
+ /^service-account[-_].*\.json$/.test(base) ||
61
+ base === 'gcp-key.json' ||
62
+ base === 'firebase-adminsdk.json');
63
+ },
64
+ },
65
+ {
66
+ rule: 'aws-credentials',
67
+ test: (p) => {
68
+ const lower = p.toLowerCase();
69
+ return (lower.endsWith('/.aws/credentials') ||
70
+ lower === '.aws/credentials' ||
71
+ lower.endsWith('aws-credentials.json'));
72
+ },
73
+ },
74
+ {
75
+ rule: 'credentials-file',
76
+ test: (p) => {
77
+ const base = basename(p).toLowerCase();
78
+ return (base === 'credentials.json' ||
79
+ base === 'secrets.json' ||
80
+ base === 'secret.json');
81
+ },
82
+ },
83
+ {
84
+ rule: 'secrets-dir',
85
+ test: (p) => /(^|\/)secrets\//i.test(p),
86
+ },
87
+ ];
88
+ // ── Content regexes ──────────────────────────────────────────────────
89
+ // High-confidence shapes only — false positives mean a frustrated user
90
+ // re-uploading after a scrub they couldn't validate. Keep the bar high.
91
+ const CONTENT_RULES = [
92
+ // Anthropic
93
+ { rule: 'anthropic-api-key', regex: /\bsk-ant-[a-zA-Z0-9-_]{20,}\b/ },
94
+ // OpenAI
95
+ { rule: 'openai-api-key', regex: /\bsk-(?:proj-)?[a-zA-Z0-9-_]{32,}\b/ },
96
+ // AWS
97
+ { rule: 'aws-access-key-id', regex: /\bAKIA[0-9A-Z]{16}\b/ },
98
+ { rule: 'aws-secret-access-key', regex: /aws_secret_access_key\s*=\s*['"]?[A-Za-z0-9/+=]{40}\b/i },
99
+ // GitHub
100
+ { rule: 'github-personal-token', regex: /\bghp_[A-Za-z0-9]{36}\b/ },
101
+ { rule: 'github-oauth-token', regex: /\bgho_[A-Za-z0-9]{36}\b/ },
102
+ { rule: 'github-fine-grained-token', regex: /\bgithub_pat_[A-Za-z0-9_]{82}\b/ },
103
+ // Stripe
104
+ { rule: 'stripe-live-key', regex: /\bsk_live_[A-Za-z0-9]{24,}\b/ },
105
+ { rule: 'stripe-restricted-key', regex: /\brk_live_[A-Za-z0-9]{24,}\b/ },
106
+ // Google API key
107
+ { rule: 'google-api-key', regex: /\bAIza[0-9A-Za-z\-_]{35}\b/ },
108
+ // Slack
109
+ { rule: 'slack-bot-token', regex: /\bxoxb-[A-Za-z0-9-]{20,}\b/ },
110
+ { rule: 'slack-user-token', regex: /\bxoxp-[A-Za-z0-9-]{20,}\b/ },
111
+ // GitLab
112
+ { rule: 'gitlab-personal-token', regex: /\bglpat-[A-Za-z0-9_-]{20}\b/ },
113
+ // Supabase / JWT-shaped service-role key (the shape, not the host).
114
+ // Has 3 base64url segments separated by '.'. We require an `eyJ`
115
+ // header (RFC 7519 typ:"JWT") to dodge random base64 strings.
116
+ {
117
+ rule: 'jwt-token',
118
+ regex: /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/,
119
+ },
120
+ ];
121
+ /**
122
+ * Run both layers across a list of files. Returns a verdict + every
123
+ * matching rule across every file. Empty findings = clean bundle.
124
+ *
125
+ * Files where `content` is missing skip the content-regex stage but
126
+ * still trip path rules.
127
+ */
128
+ export function scanForSecrets(files) {
129
+ const findings = [];
130
+ for (const file of files) {
131
+ // Path-based rules
132
+ for (const { rule, test } of PATH_RULES) {
133
+ if (test(file.path)) {
134
+ findings.push({ path: file.path, rule });
135
+ }
136
+ }
137
+ // Content-based rules — only on text. We could be smarter (e.g.
138
+ // skip files >1MB) but the worker already caps bundle size and
139
+ // skips known-binary extensions before getting here.
140
+ if (typeof file.content === 'string' && file.content.length > 0) {
141
+ // Split once for line numbers; the regex tests run against the
142
+ // full body so multiline shapes still match.
143
+ const lines = file.content.split('\n');
144
+ for (const { rule, regex } of CONTENT_RULES) {
145
+ // Reset stateful regexes — we don't use /g but be defensive.
146
+ const localRegex = new RegExp(regex.source, regex.flags.replace('g', ''));
147
+ for (let i = 0; i < lines.length; i++) {
148
+ if (localRegex.test(lines[i] ?? '')) {
149
+ findings.push({ path: file.path, rule, line: i + 1 });
150
+ break;
151
+ }
152
+ }
153
+ }
154
+ }
155
+ }
156
+ return {
157
+ status: findings.length > 0 ? 'rejected' : 'clean',
158
+ findings,
159
+ };
160
+ }
161
+ /**
162
+ * Whether a path likely points at a text-readable file (worth running
163
+ * content regexes against). Used by callers to filter the input list
164
+ * before they bother loading binary blobs.
165
+ */
166
+ export function isLikelyTextPath(path) {
167
+ const ext = path.toLowerCase().split('.').pop() ?? '';
168
+ return (TEXT_EXTENSIONS.has(ext) ||
169
+ // Files without an extension that we'd still want to scan
170
+ path.endsWith('Dockerfile') ||
171
+ path.endsWith('Makefile') ||
172
+ /\.env(\.|$)/.test(path));
173
+ }
174
+ const TEXT_EXTENSIONS = new Set([
175
+ 'md',
176
+ 'mdx',
177
+ 'txt',
178
+ 'json',
179
+ 'yaml',
180
+ 'yml',
181
+ 'toml',
182
+ 'ini',
183
+ 'cfg',
184
+ 'conf',
185
+ 'env',
186
+ 'js',
187
+ 'jsx',
188
+ 'ts',
189
+ 'tsx',
190
+ 'mjs',
191
+ 'cjs',
192
+ 'py',
193
+ 'rb',
194
+ 'go',
195
+ 'rs',
196
+ 'sh',
197
+ 'bash',
198
+ 'zsh',
199
+ 'fish',
200
+ 'sql',
201
+ 'graphql',
202
+ 'gql',
203
+ 'html',
204
+ 'xml',
205
+ 'svg',
206
+ 'css',
207
+ 'scss',
208
+ 'less',
209
+ 'lock',
210
+ ]);
211
+ function basename(p) {
212
+ const i = p.lastIndexOf('/');
213
+ return i === -1 ? p : p.slice(i + 1);
214
+ }
@@ -14,4 +14,5 @@ export * from './skill-pr.schema.js';
14
14
  export * from './intelligence.schema.js';
15
15
  export * from './api-keys.schema.js';
16
16
  export * from './skill-report.schema.js';
17
+ export * from './run.schema.js';
17
18
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schemas/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAA;AACxC,cAAc,mBAAmB,CAAA;AACjC,cAAc,2BAA2B,CAAA;AACzC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,oBAAoB,CAAA;AAClC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,8BAA8B,CAAA;AAC5C,cAAc,wBAAwB,CAAA;AACtC,cAAc,yBAAyB,CAAA;AACvC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,sBAAsB,CAAA;AACpC,cAAc,0BAA0B,CAAA;AACxC,cAAc,sBAAsB,CAAA;AACpC,cAAc,0BAA0B,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schemas/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAA;AACxC,cAAc,mBAAmB,CAAA;AACjC,cAAc,2BAA2B,CAAA;AACzC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,oBAAoB,CAAA;AAClC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,8BAA8B,CAAA;AAC5C,cAAc,wBAAwB,CAAA;AACtC,cAAc,yBAAyB,CAAA;AACvC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,sBAAsB,CAAA;AACpC,cAAc,0BAA0B,CAAA;AACxC,cAAc,sBAAsB,CAAA;AACpC,cAAc,0BAA0B,CAAA;AACxC,cAAc,iBAAiB,CAAA"}
@@ -14,3 +14,4 @@ export * from './skill-pr.schema.js';
14
14
  export * from './intelligence.schema.js';
15
15
  export * from './api-keys.schema.js';
16
16
  export * from './skill-report.schema.js';
17
+ export * from './run.schema.js';
@@ -0,0 +1,501 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Run schemas — server-side scheduled Skill executions.
4
+ *
5
+ * The on-disk shape is split across three tables:
6
+ *
7
+ * - `skill_bundles` uploaded project folder (SKILL.md + scripts + .env)
8
+ * - `runs` schedule + agent + status for one deployment
9
+ * - `run_executions` append-only fire-history with logs and tokens
10
+ *
11
+ * (See supabase/migrations/049_runs_and_bundles.sql for the full
12
+ * column-level docstrings.)
13
+ */
14
+ export declare const runAgentSchema: z.ZodEnum<["claude", "codex", "cursor", "gemini", "cline", "amp"]>;
15
+ export type RunAgent = z.infer<typeof runAgentSchema>;
16
+ export declare const runScheduleKindSchema: z.ZodEnum<["hourly", "daily", "weekly", "monthly", "custom"]>;
17
+ export type RunScheduleKind = z.infer<typeof runScheduleKindSchema>;
18
+ export declare const runStatusSchema: z.ZodEnum<["active", "paused", "failed", "disabled"]>;
19
+ export type RunStatus = z.infer<typeof runStatusSchema>;
20
+ export declare const runExecutionStatusSchema: z.ZodEnum<["running", "success", "failed", "timeout", "cancelled"]>;
21
+ export type RunExecutionStatus = z.infer<typeof runExecutionStatusSchema>;
22
+ export declare const runScheduleInputSchema: z.ZodDiscriminatedUnion<"kind", [z.ZodObject<{
23
+ kind: z.ZodLiteral<"hourly">;
24
+ }, "strip", z.ZodTypeAny, {
25
+ kind: "hourly";
26
+ }, {
27
+ kind: "hourly";
28
+ }>, z.ZodObject<{
29
+ kind: z.ZodLiteral<"daily">;
30
+ time: z.ZodString;
31
+ }, "strip", z.ZodTypeAny, {
32
+ kind: "daily";
33
+ time: string;
34
+ }, {
35
+ kind: "daily";
36
+ time: string;
37
+ }>, z.ZodObject<{
38
+ kind: z.ZodLiteral<"weekly">;
39
+ time: z.ZodString;
40
+ weekday: z.ZodNumber;
41
+ }, "strip", z.ZodTypeAny, {
42
+ kind: "weekly";
43
+ time: string;
44
+ weekday: number;
45
+ }, {
46
+ kind: "weekly";
47
+ time: string;
48
+ weekday: number;
49
+ }>, z.ZodObject<{
50
+ kind: z.ZodLiteral<"monthly">;
51
+ time: z.ZodString;
52
+ day_of_month: z.ZodNumber;
53
+ }, "strip", z.ZodTypeAny, {
54
+ kind: "monthly";
55
+ time: string;
56
+ day_of_month: number;
57
+ }, {
58
+ kind: "monthly";
59
+ time: string;
60
+ day_of_month: number;
61
+ }>, z.ZodObject<{
62
+ kind: z.ZodLiteral<"custom">;
63
+ cron_expr: z.ZodString;
64
+ }, "strip", z.ZodTypeAny, {
65
+ kind: "custom";
66
+ cron_expr: string;
67
+ }, {
68
+ kind: "custom";
69
+ cron_expr: string;
70
+ }>]>;
71
+ export type RunScheduleInput = z.infer<typeof runScheduleInputSchema>;
72
+ export declare const skillBundleSchema: z.ZodObject<{
73
+ id: z.ZodString;
74
+ owner_id: z.ZodString;
75
+ skill_id: z.ZodNullable<z.ZodString>;
76
+ source: z.ZodEnum<["upload", "github"]>;
77
+ tarball_path: z.ZodString;
78
+ manifest: z.ZodArray<z.ZodObject<{
79
+ path: z.ZodString;
80
+ size: z.ZodNumber;
81
+ sha256: z.ZodString;
82
+ kind: z.ZodEnum<["text", "binary", "script"]>;
83
+ }, "strip", z.ZodTypeAny, {
84
+ path: string;
85
+ kind: "text" | "binary" | "script";
86
+ size: number;
87
+ sha256: string;
88
+ }, {
89
+ path: string;
90
+ kind: "text" | "binary" | "script";
91
+ size: number;
92
+ sha256: string;
93
+ }>, "many">;
94
+ total_size_bytes: z.ZodNumber;
95
+ has_scripts: z.ZodBoolean;
96
+ has_env_template: z.ZodBoolean;
97
+ secret_scan_status: z.ZodEnum<["pending", "clean", "rejected"]>;
98
+ secret_scan_findings: z.ZodNullable<z.ZodArray<z.ZodObject<{
99
+ path: z.ZodString;
100
+ rule: z.ZodString;
101
+ line: z.ZodOptional<z.ZodNumber>;
102
+ }, "strip", z.ZodTypeAny, {
103
+ path: string;
104
+ rule: string;
105
+ line?: number | undefined;
106
+ }, {
107
+ path: string;
108
+ rule: string;
109
+ line?: number | undefined;
110
+ }>, "many">>;
111
+ created_at: z.ZodString;
112
+ updated_at: z.ZodString;
113
+ }, "strip", z.ZodTypeAny, {
114
+ id: string;
115
+ skill_id: string | null;
116
+ created_at: string;
117
+ updated_at: string;
118
+ owner_id: string;
119
+ source: "upload" | "github";
120
+ tarball_path: string;
121
+ manifest: {
122
+ path: string;
123
+ kind: "text" | "binary" | "script";
124
+ size: number;
125
+ sha256: string;
126
+ }[];
127
+ total_size_bytes: number;
128
+ has_scripts: boolean;
129
+ has_env_template: boolean;
130
+ secret_scan_status: "pending" | "clean" | "rejected";
131
+ secret_scan_findings: {
132
+ path: string;
133
+ rule: string;
134
+ line?: number | undefined;
135
+ }[] | null;
136
+ }, {
137
+ id: string;
138
+ skill_id: string | null;
139
+ created_at: string;
140
+ updated_at: string;
141
+ owner_id: string;
142
+ source: "upload" | "github";
143
+ tarball_path: string;
144
+ manifest: {
145
+ path: string;
146
+ kind: "text" | "binary" | "script";
147
+ size: number;
148
+ sha256: string;
149
+ }[];
150
+ total_size_bytes: number;
151
+ has_scripts: boolean;
152
+ has_env_template: boolean;
153
+ secret_scan_status: "pending" | "clean" | "rejected";
154
+ secret_scan_findings: {
155
+ path: string;
156
+ rule: string;
157
+ line?: number | undefined;
158
+ }[] | null;
159
+ }>;
160
+ export type SkillBundle = z.infer<typeof skillBundleSchema>;
161
+ export declare const runSchema: z.ZodObject<{
162
+ id: z.ZodString;
163
+ owner_id: z.ZodString;
164
+ bundle_id: z.ZodString;
165
+ slug: z.ZodString;
166
+ name: z.ZodString;
167
+ agent: z.ZodEnum<["claude", "codex", "cursor", "gemini", "cline", "amp"]>;
168
+ schedule_kind: z.ZodEnum<["hourly", "daily", "weekly", "monthly", "custom"]>;
169
+ cron_expr: z.ZodString;
170
+ timezone: z.ZodString;
171
+ status: z.ZodEnum<["active", "paused", "failed", "disabled"]>;
172
+ timeout_seconds: z.ZodNumber;
173
+ max_log_bytes: z.ZodNumber;
174
+ next_run_at: z.ZodNullable<z.ZodString>;
175
+ last_run_at: z.ZodNullable<z.ZodString>;
176
+ last_exec_status: z.ZodNullable<z.ZodEnum<["success", "failed", "timeout", "cancelled"]>>;
177
+ total_runs: z.ZodNumber;
178
+ total_failures: z.ZodNumber;
179
+ created_at: z.ZodString;
180
+ updated_at: z.ZodString;
181
+ }, "strip", z.ZodTypeAny, {
182
+ status: "failed" | "active" | "paused" | "disabled";
183
+ id: string;
184
+ created_at: string;
185
+ updated_at: string;
186
+ slug: string;
187
+ name: string;
188
+ owner_id: string;
189
+ cron_expr: string;
190
+ bundle_id: string;
191
+ agent: "claude" | "codex" | "cursor" | "gemini" | "cline" | "amp";
192
+ schedule_kind: "custom" | "monthly" | "hourly" | "daily" | "weekly";
193
+ timezone: string;
194
+ timeout_seconds: number;
195
+ max_log_bytes: number;
196
+ next_run_at: string | null;
197
+ last_run_at: string | null;
198
+ last_exec_status: "success" | "failed" | "timeout" | "cancelled" | null;
199
+ total_runs: number;
200
+ total_failures: number;
201
+ }, {
202
+ status: "failed" | "active" | "paused" | "disabled";
203
+ id: string;
204
+ created_at: string;
205
+ updated_at: string;
206
+ slug: string;
207
+ name: string;
208
+ owner_id: string;
209
+ cron_expr: string;
210
+ bundle_id: string;
211
+ agent: "claude" | "codex" | "cursor" | "gemini" | "cline" | "amp";
212
+ schedule_kind: "custom" | "monthly" | "hourly" | "daily" | "weekly";
213
+ timezone: string;
214
+ timeout_seconds: number;
215
+ max_log_bytes: number;
216
+ next_run_at: string | null;
217
+ last_run_at: string | null;
218
+ last_exec_status: "success" | "failed" | "timeout" | "cancelled" | null;
219
+ total_runs: number;
220
+ total_failures: number;
221
+ }>;
222
+ export type Run = z.infer<typeof runSchema>;
223
+ export declare const createRunInputSchema: z.ZodObject<{
224
+ bundle_id: z.ZodString;
225
+ name: z.ZodString;
226
+ agent: z.ZodEnum<["claude", "codex", "cursor", "gemini", "cline", "amp"]>;
227
+ schedule: z.ZodDiscriminatedUnion<"kind", [z.ZodObject<{
228
+ kind: z.ZodLiteral<"hourly">;
229
+ }, "strip", z.ZodTypeAny, {
230
+ kind: "hourly";
231
+ }, {
232
+ kind: "hourly";
233
+ }>, z.ZodObject<{
234
+ kind: z.ZodLiteral<"daily">;
235
+ time: z.ZodString;
236
+ }, "strip", z.ZodTypeAny, {
237
+ kind: "daily";
238
+ time: string;
239
+ }, {
240
+ kind: "daily";
241
+ time: string;
242
+ }>, z.ZodObject<{
243
+ kind: z.ZodLiteral<"weekly">;
244
+ time: z.ZodString;
245
+ weekday: z.ZodNumber;
246
+ }, "strip", z.ZodTypeAny, {
247
+ kind: "weekly";
248
+ time: string;
249
+ weekday: number;
250
+ }, {
251
+ kind: "weekly";
252
+ time: string;
253
+ weekday: number;
254
+ }>, z.ZodObject<{
255
+ kind: z.ZodLiteral<"monthly">;
256
+ time: z.ZodString;
257
+ day_of_month: z.ZodNumber;
258
+ }, "strip", z.ZodTypeAny, {
259
+ kind: "monthly";
260
+ time: string;
261
+ day_of_month: number;
262
+ }, {
263
+ kind: "monthly";
264
+ time: string;
265
+ day_of_month: number;
266
+ }>, z.ZodObject<{
267
+ kind: z.ZodLiteral<"custom">;
268
+ cron_expr: z.ZodString;
269
+ }, "strip", z.ZodTypeAny, {
270
+ kind: "custom";
271
+ cron_expr: string;
272
+ }, {
273
+ kind: "custom";
274
+ cron_expr: string;
275
+ }>]>;
276
+ timezone: z.ZodDefault<z.ZodString>;
277
+ timeout_seconds: z.ZodOptional<z.ZodNumber>;
278
+ }, "strip", z.ZodTypeAny, {
279
+ name: string;
280
+ bundle_id: string;
281
+ agent: "claude" | "codex" | "cursor" | "gemini" | "cline" | "amp";
282
+ timezone: string;
283
+ schedule: {
284
+ kind: "hourly";
285
+ } | {
286
+ kind: "daily";
287
+ time: string;
288
+ } | {
289
+ kind: "weekly";
290
+ time: string;
291
+ weekday: number;
292
+ } | {
293
+ kind: "monthly";
294
+ time: string;
295
+ day_of_month: number;
296
+ } | {
297
+ kind: "custom";
298
+ cron_expr: string;
299
+ };
300
+ timeout_seconds?: number | undefined;
301
+ }, {
302
+ name: string;
303
+ bundle_id: string;
304
+ agent: "claude" | "codex" | "cursor" | "gemini" | "cline" | "amp";
305
+ schedule: {
306
+ kind: "hourly";
307
+ } | {
308
+ kind: "daily";
309
+ time: string;
310
+ } | {
311
+ kind: "weekly";
312
+ time: string;
313
+ weekday: number;
314
+ } | {
315
+ kind: "monthly";
316
+ time: string;
317
+ day_of_month: number;
318
+ } | {
319
+ kind: "custom";
320
+ cron_expr: string;
321
+ };
322
+ timezone?: string | undefined;
323
+ timeout_seconds?: number | undefined;
324
+ }>;
325
+ export type CreateRunInput = z.infer<typeof createRunInputSchema>;
326
+ export declare const updateRunInputSchema: z.ZodObject<{
327
+ name: z.ZodOptional<z.ZodString>;
328
+ agent: z.ZodOptional<z.ZodEnum<["claude", "codex", "cursor", "gemini", "cline", "amp"]>>;
329
+ schedule: z.ZodOptional<z.ZodDiscriminatedUnion<"kind", [z.ZodObject<{
330
+ kind: z.ZodLiteral<"hourly">;
331
+ }, "strip", z.ZodTypeAny, {
332
+ kind: "hourly";
333
+ }, {
334
+ kind: "hourly";
335
+ }>, z.ZodObject<{
336
+ kind: z.ZodLiteral<"daily">;
337
+ time: z.ZodString;
338
+ }, "strip", z.ZodTypeAny, {
339
+ kind: "daily";
340
+ time: string;
341
+ }, {
342
+ kind: "daily";
343
+ time: string;
344
+ }>, z.ZodObject<{
345
+ kind: z.ZodLiteral<"weekly">;
346
+ time: z.ZodString;
347
+ weekday: z.ZodNumber;
348
+ }, "strip", z.ZodTypeAny, {
349
+ kind: "weekly";
350
+ time: string;
351
+ weekday: number;
352
+ }, {
353
+ kind: "weekly";
354
+ time: string;
355
+ weekday: number;
356
+ }>, z.ZodObject<{
357
+ kind: z.ZodLiteral<"monthly">;
358
+ time: z.ZodString;
359
+ day_of_month: z.ZodNumber;
360
+ }, "strip", z.ZodTypeAny, {
361
+ kind: "monthly";
362
+ time: string;
363
+ day_of_month: number;
364
+ }, {
365
+ kind: "monthly";
366
+ time: string;
367
+ day_of_month: number;
368
+ }>, z.ZodObject<{
369
+ kind: z.ZodLiteral<"custom">;
370
+ cron_expr: z.ZodString;
371
+ }, "strip", z.ZodTypeAny, {
372
+ kind: "custom";
373
+ cron_expr: string;
374
+ }, {
375
+ kind: "custom";
376
+ cron_expr: string;
377
+ }>]>>;
378
+ timezone: z.ZodOptional<z.ZodString>;
379
+ timeout_seconds: z.ZodOptional<z.ZodNumber>;
380
+ status: z.ZodOptional<z.ZodEnum<["active", "paused"]>>;
381
+ }, "strip", z.ZodTypeAny, {
382
+ status?: "active" | "paused" | undefined;
383
+ name?: string | undefined;
384
+ agent?: "claude" | "codex" | "cursor" | "gemini" | "cline" | "amp" | undefined;
385
+ timezone?: string | undefined;
386
+ timeout_seconds?: number | undefined;
387
+ schedule?: {
388
+ kind: "hourly";
389
+ } | {
390
+ kind: "daily";
391
+ time: string;
392
+ } | {
393
+ kind: "weekly";
394
+ time: string;
395
+ weekday: number;
396
+ } | {
397
+ kind: "monthly";
398
+ time: string;
399
+ day_of_month: number;
400
+ } | {
401
+ kind: "custom";
402
+ cron_expr: string;
403
+ } | undefined;
404
+ }, {
405
+ status?: "active" | "paused" | undefined;
406
+ name?: string | undefined;
407
+ agent?: "claude" | "codex" | "cursor" | "gemini" | "cline" | "amp" | undefined;
408
+ timezone?: string | undefined;
409
+ timeout_seconds?: number | undefined;
410
+ schedule?: {
411
+ kind: "hourly";
412
+ } | {
413
+ kind: "daily";
414
+ time: string;
415
+ } | {
416
+ kind: "weekly";
417
+ time: string;
418
+ weekday: number;
419
+ } | {
420
+ kind: "monthly";
421
+ time: string;
422
+ day_of_month: number;
423
+ } | {
424
+ kind: "custom";
425
+ cron_expr: string;
426
+ } | undefined;
427
+ }>;
428
+ export type UpdateRunInput = z.infer<typeof updateRunInputSchema>;
429
+ export declare const runExecutionSchema: z.ZodObject<{
430
+ id: z.ZodString;
431
+ run_id: z.ZodString;
432
+ worker_id: z.ZodNullable<z.ZodString>;
433
+ status: z.ZodEnum<["running", "success", "failed", "timeout", "cancelled"]>;
434
+ started_at: z.ZodString;
435
+ finished_at: z.ZodNullable<z.ZodString>;
436
+ duration_ms: z.ZodNullable<z.ZodNumber>;
437
+ exit_code: z.ZodNullable<z.ZodNumber>;
438
+ log_text: z.ZodNullable<z.ZodString>;
439
+ log_truncated: z.ZodBoolean;
440
+ input_tokens: z.ZodNullable<z.ZodNumber>;
441
+ output_tokens: z.ZodNullable<z.ZodNumber>;
442
+ cost_estimate_cents: z.ZodNullable<z.ZodNumber>;
443
+ error_message: z.ZodNullable<z.ZodString>;
444
+ created_at: z.ZodString;
445
+ }, "strip", z.ZodTypeAny, {
446
+ status: "success" | "failed" | "running" | "timeout" | "cancelled";
447
+ id: string;
448
+ created_at: string;
449
+ input_tokens: number | null;
450
+ output_tokens: number | null;
451
+ error_message: string | null;
452
+ run_id: string;
453
+ worker_id: string | null;
454
+ started_at: string;
455
+ finished_at: string | null;
456
+ duration_ms: number | null;
457
+ exit_code: number | null;
458
+ log_text: string | null;
459
+ log_truncated: boolean;
460
+ cost_estimate_cents: number | null;
461
+ }, {
462
+ status: "success" | "failed" | "running" | "timeout" | "cancelled";
463
+ id: string;
464
+ created_at: string;
465
+ input_tokens: number | null;
466
+ output_tokens: number | null;
467
+ error_message: string | null;
468
+ run_id: string;
469
+ worker_id: string | null;
470
+ started_at: string;
471
+ finished_at: string | null;
472
+ duration_ms: number | null;
473
+ exit_code: number | null;
474
+ log_text: string | null;
475
+ log_truncated: boolean;
476
+ cost_estimate_cents: number | null;
477
+ }>;
478
+ export type RunExecution = z.infer<typeof runExecutionSchema>;
479
+ export declare const deploySessionSchema: z.ZodObject<{
480
+ session_id: z.ZodString;
481
+ upload_url: z.ZodString;
482
+ wizard_url: z.ZodString;
483
+ expires_at: z.ZodString;
484
+ }, "strip", z.ZodTypeAny, {
485
+ expires_at: string;
486
+ session_id: string;
487
+ upload_url: string;
488
+ wizard_url: string;
489
+ }, {
490
+ expires_at: string;
491
+ session_id: string;
492
+ upload_url: string;
493
+ wizard_url: string;
494
+ }>;
495
+ export type DeploySession = z.infer<typeof deploySessionSchema>;
496
+ export interface RunExecuteJob {
497
+ run_id: string;
498
+ execution_id: string;
499
+ scheduled_at: string;
500
+ }
501
+ //# sourceMappingURL=run.schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.schema.d.ts","sourceRoot":"","sources":["../../src/schemas/run.schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB;;;;;;;;;;;GAWG;AAKH,eAAO,MAAM,cAAc,oEAOzB,CAAA;AACF,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAA;AAKrD,eAAO,MAAM,qBAAqB,+DAMhC,CAAA;AACF,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAA;AAEnE,eAAO,MAAM,eAAe,uDAK1B,CAAA;AACF,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAA;AAEvD,eAAO,MAAM,wBAAwB,qEAMnC,CAAA;AACF,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAA;AAezE,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAsBjC,CAAA;AACF,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAA;AAGrE,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA6B5B,CAAA;AACF,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAA;AAG3D,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsBpB,CAAA;AACF,MAAM,MAAM,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAA;AAE3C,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAO/B,CAAA;AACF,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAA;AAEjE,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAO/B,CAAA;AACF,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAA;AAGjE,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgB7B,CAAA;AACF,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAA;AAO7D,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;EAK9B,CAAA;AACF,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAG/D,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,EAAE,MAAM,CAAA;CACrB"}
@@ -0,0 +1,179 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Run schemas — server-side scheduled Skill executions.
4
+ *
5
+ * The on-disk shape is split across three tables:
6
+ *
7
+ * - `skill_bundles` uploaded project folder (SKILL.md + scripts + .env)
8
+ * - `runs` schedule + agent + status for one deployment
9
+ * - `run_executions` append-only fire-history with logs and tokens
10
+ *
11
+ * (See supabase/migrations/049_runs_and_bundles.sql for the full
12
+ * column-level docstrings.)
13
+ */
14
+ // ── Agent identifier ─────────────────────────────────────────────────
15
+ // Same string set as the existing multi-agent feature. The runner
16
+ // picks the matching CLI invocation per name.
17
+ export const runAgentSchema = z.enum([
18
+ 'claude',
19
+ 'codex',
20
+ 'cursor',
21
+ 'gemini',
22
+ 'cline',
23
+ 'amp',
24
+ ]);
25
+ // ── Schedule presets ─────────────────────────────────────────────────
26
+ // The wizard picker only shows the presets; `custom` is a power-user
27
+ // affordance exposed via the same form's "Advanced" twirl-down.
28
+ export const runScheduleKindSchema = z.enum([
29
+ 'hourly',
30
+ 'daily',
31
+ 'weekly',
32
+ 'monthly',
33
+ 'custom',
34
+ ]);
35
+ export const runStatusSchema = z.enum([
36
+ 'active',
37
+ 'paused',
38
+ 'failed',
39
+ 'disabled',
40
+ ]);
41
+ export const runExecutionStatusSchema = z.enum([
42
+ 'running',
43
+ 'success',
44
+ 'failed',
45
+ 'timeout',
46
+ 'cancelled',
47
+ ]);
48
+ // ── Schedule input ───────────────────────────────────────────────────
49
+ //
50
+ // The wizard sends one of:
51
+ //
52
+ // { kind: 'hourly' } → "0 * * * *"
53
+ // { kind: 'daily', time: '08:00' } → "0 8 * * *"
54
+ // { kind: 'weekly', time: '08:00', weekday: 1 } → "0 8 * * 1"
55
+ // { kind: 'monthly', time: '08:00', day_of_month: 1 } → "0 8 1 * *"
56
+ // { kind: 'custom', cron_expr: '*/15 * * * *' }
57
+ //
58
+ // The API service expands these into the canonical 5-field cron used
59
+ // downstream. Timezone is captured separately so "daily at 08:00" lands
60
+ // at the user's local 08:00 regardless of server location.
61
+ export const runScheduleInputSchema = z.discriminatedUnion('kind', [
62
+ z.object({
63
+ kind: z.literal('hourly'),
64
+ }),
65
+ z.object({
66
+ kind: z.literal('daily'),
67
+ time: z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/, 'HH:MM'),
68
+ }),
69
+ z.object({
70
+ kind: z.literal('weekly'),
71
+ time: z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/, 'HH:MM'),
72
+ weekday: z.number().int().min(0).max(6),
73
+ }),
74
+ z.object({
75
+ kind: z.literal('monthly'),
76
+ time: z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/, 'HH:MM'),
77
+ day_of_month: z.number().int().min(1).max(28),
78
+ }),
79
+ z.object({
80
+ kind: z.literal('custom'),
81
+ cron_expr: z.string().min(9).max(120),
82
+ }),
83
+ ]);
84
+ // ── Bundle ───────────────────────────────────────────────────────────
85
+ export const skillBundleSchema = z.object({
86
+ id: z.string().uuid(),
87
+ owner_id: z.string().uuid(),
88
+ skill_id: z.string().uuid().nullable(),
89
+ source: z.enum(['upload', 'github']),
90
+ tarball_path: z.string(),
91
+ manifest: z.array(z.object({
92
+ path: z.string(),
93
+ size: z.number().int().nonnegative(),
94
+ sha256: z.string(),
95
+ kind: z.enum(['text', 'binary', 'script']),
96
+ })),
97
+ total_size_bytes: z.number().int().nonnegative(),
98
+ has_scripts: z.boolean(),
99
+ has_env_template: z.boolean(),
100
+ secret_scan_status: z.enum(['pending', 'clean', 'rejected']),
101
+ secret_scan_findings: z
102
+ .array(z.object({
103
+ path: z.string(),
104
+ rule: z.string(),
105
+ line: z.number().int().nonnegative().optional(),
106
+ }))
107
+ .nullable(),
108
+ created_at: z.string().datetime(),
109
+ updated_at: z.string().datetime(),
110
+ });
111
+ // ── Run ──────────────────────────────────────────────────────────────
112
+ export const runSchema = z.object({
113
+ id: z.string().uuid(),
114
+ owner_id: z.string().uuid(),
115
+ bundle_id: z.string().uuid(),
116
+ slug: z.string(),
117
+ name: z.string(),
118
+ agent: runAgentSchema,
119
+ schedule_kind: runScheduleKindSchema,
120
+ cron_expr: z.string(),
121
+ timezone: z.string(),
122
+ status: runStatusSchema,
123
+ timeout_seconds: z.number().int().positive(),
124
+ max_log_bytes: z.number().int().positive(),
125
+ next_run_at: z.string().datetime().nullable(),
126
+ last_run_at: z.string().datetime().nullable(),
127
+ last_exec_status: z
128
+ .enum(['success', 'failed', 'timeout', 'cancelled'])
129
+ .nullable(),
130
+ total_runs: z.number().int().nonnegative(),
131
+ total_failures: z.number().int().nonnegative(),
132
+ created_at: z.string().datetime(),
133
+ updated_at: z.string().datetime(),
134
+ });
135
+ export const createRunInputSchema = z.object({
136
+ bundle_id: z.string().uuid(),
137
+ name: z.string().min(1).max(120),
138
+ agent: runAgentSchema,
139
+ schedule: runScheduleInputSchema,
140
+ timezone: z.string().min(2).max(60).default('UTC'),
141
+ timeout_seconds: z.number().int().min(5).max(300).optional(),
142
+ });
143
+ export const updateRunInputSchema = z.object({
144
+ name: z.string().min(1).max(120).optional(),
145
+ agent: runAgentSchema.optional(),
146
+ schedule: runScheduleInputSchema.optional(),
147
+ timezone: z.string().optional(),
148
+ timeout_seconds: z.number().int().min(5).max(300).optional(),
149
+ status: z.enum(['active', 'paused']).optional(),
150
+ });
151
+ // ── Execution ────────────────────────────────────────────────────────
152
+ export const runExecutionSchema = z.object({
153
+ id: z.string().uuid(),
154
+ run_id: z.string().uuid(),
155
+ worker_id: z.string().nullable(),
156
+ status: runExecutionStatusSchema,
157
+ started_at: z.string().datetime(),
158
+ finished_at: z.string().datetime().nullable(),
159
+ duration_ms: z.number().int().nullable(),
160
+ exit_code: z.number().int().nullable(),
161
+ log_text: z.string().nullable(),
162
+ log_truncated: z.boolean(),
163
+ input_tokens: z.number().int().nullable(),
164
+ output_tokens: z.number().int().nullable(),
165
+ cost_estimate_cents: z.number().int().nullable(),
166
+ error_message: z.string().nullable(),
167
+ created_at: z.string().datetime(),
168
+ });
169
+ // ── Deploy-CLI session ──────────────────────────────────────────────
170
+ // The CLI's `prave deploy` flow uses a short-lived session token to
171
+ // hand off the upload from the terminal to the browser wizard. The
172
+ // API mints the session, the CLI uploads the tarball, the browser
173
+ // claims the session and finalizes the Run.
174
+ export const deploySessionSchema = z.object({
175
+ session_id: z.string(),
176
+ upload_url: z.string().url(),
177
+ wizard_url: z.string().url(),
178
+ expires_at: z.string().datetime(),
179
+ });
@@ -125,6 +125,26 @@ export interface PlanLimits {
125
125
  has_priority_support: boolean;
126
126
  /** Early access to in-development features. */
127
127
  has_early_access: boolean;
128
+ /**
129
+ * Max number of active Runs (deployed schedules) per user. 0 = the
130
+ * feature is locked behind an upgrade — the user can still browse
131
+ * the section but the "Schedule a run" CTA opens an upgrade modal.
132
+ */
133
+ runs_max_active: number;
134
+ /**
135
+ * Smallest schedule interval the user can pick. Strings map to the
136
+ * wizard's preset list:
137
+ * 'none' — Runs feature disabled entirely
138
+ * 'daily' — daily / weekly / monthly only
139
+ * 'hourly' — adds the hourly preset (Max-tier)
140
+ * 'custom' — adds the custom-cron field (Max-tier)
141
+ * The UI uses this to hide presets the plan doesn't allow.
142
+ */
143
+ runs_min_frequency: 'none' | 'daily' | 'hourly' | 'custom';
144
+ /** Hard timeout cap (seconds) per single execution. */
145
+ runs_max_timeout_seconds: number;
146
+ /** Max stdout/stderr captured per execution (bytes). */
147
+ runs_max_log_bytes: number;
128
148
  }
129
149
  export declare const PLAN_LIMITS: Record<Plan, PlanLimits>;
130
150
  export declare const PLAN_RANK: Record<Plan, number>;
@@ -1 +1 @@
1
- {"version":3,"file":"plan-limits.d.ts","sourceRoot":"","sources":["../../src/types/plan-limits.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"plan-limits.d.ts","sourceRoot":"","sources":["../../src/types/plan-limits.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,8BAA8B,CAAA;AAExD;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,UAAU;IACzB,+EAA+E;IAC/E,KAAK,EAAE,MAAM,CAAA;IACb,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAA;IACf,2EAA2E;IAC3E,iBAAiB,EAAE,MAAM,CAAA;IACzB,gEAAgE;IAChE,gBAAgB,EAAE,MAAM,CAAA;IACxB,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAA;IACZ,0DAA0D;IAC1D,SAAS,EAAE,OAAO,CAAA;IAGlB,+EAA+E;IAC/E,YAAY,EAAE,OAAO,CAAA;IACrB,8BAA8B;IAC9B,YAAY,EAAE,OAAO,CAAA;IACrB;;;;;;;;OAQG;IACH,mBAAmB,EAAE,OAAO,CAAA;IAG5B;;;OAGG;IACH,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAA;IACpC,mEAAmE;IACnE,mBAAmB,EAAE,aAAa,CAAC,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,KAAK,CAAC,CAAA;IAC9F,4BAA4B;IAC5B,YAAY,EAAE,OAAO,CAAA;IACrB,wDAAwD;IACxD,cAAc,EAAE,OAAO,CAAA;IACvB,yCAAyC;IACzC,sBAAsB,EAAE,OAAO,CAAA;IAG/B,0CAA0C;IAC1C,oBAAoB,EAAE,OAAO,CAAA;IAC7B,0CAA0C;IAC1C,qBAAqB,EAAE,OAAO,CAAA;IAC9B;;OAEG;IACH,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAA;IACnC,4BAA4B;IAC5B,cAAc,EAAE,OAAO,CAAA;IACvB,oDAAoD;IACpD,kBAAkB,EAAE,OAAO,CAAA;IAC3B,+CAA+C;IAC/C,iBAAiB,EAAE,OAAO,CAAA;IAG1B;;;;OAIG;IACH,sBAAsB,EAAE,MAAM,CAAA;IAC9B,gDAAgD;IAChD,mBAAmB,EAAE,MAAM,CAAA;IAC3B;;;OAGG;IACH,uBAAuB,EAAE,MAAM,CAAA;IAC/B,6EAA6E;IAC7E,kBAAkB,EAAE,OAAO,CAAA;IAG3B,gDAAgD;IAChD,gBAAgB,EAAE,OAAO,CAAA;IACzB;;;;;OAKG;IACH,wBAAwB,EAAE,MAAM,GAAG,IAAI,CAAA;IACvC;;;;;OAKG;IACH,wBAAwB,EAAE,MAAM,GAAG,IAAI,CAAA;IACvC,uDAAuD;IACvD,aAAa,EAAE,OAAO,CAAA;IACtB,qCAAqC;IACrC,kBAAkB,EAAE,OAAO,CAAA;IAC3B;;;OAGG;IACH,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,mCAAmC;IACnC,gBAAgB,EAAE,OAAO,CAAA;IAGzB,8CAA8C;IAC9C,kBAAkB,EAAE,OAAO,CAAA;IAC3B;;;OAGG;IACH,oBAAoB,EAAE,OAAO,CAAA;IAC7B,oDAAoD;IACpD,kBAAkB,EAAE,OAAO,CAAA;IAC3B,mEAAmE;IACnE,kBAAkB,EAAE,OAAO,CAAA;IAG3B,6CAA6C;IAC7C,oBAAoB,EAAE,OAAO,CAAA;IAC7B,+CAA+C;IAC/C,gBAAgB,EAAE,OAAO,CAAA;IAGzB;;;;OAIG;IACH,eAAe,EAAE,MAAM,CAAA;IACvB;;;;;;;;OAQG;IACH,kBAAkB,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAA;IAC1D,uDAAuD;IACvD,wBAAwB,EAAE,MAAM,CAAA;IAChC,wDAAwD;IACxD,kBAAkB,EAAE,MAAM,CAAA;CAC3B;AAID,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,IAAI,EAAE,UAAU,CAoLhD,CAAA;AAED,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAI1C,CAAA;AAED,8DAA8D;AAC9D,eAAO,MAAM,SAAS,GAAI,QAAQ,IAAI,EAAE,UAAU,IAAI,KAAG,OACf,CAAA;AAE1C;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,GAAI,SAAS,MAAM,UAAU,KAAG,IAAI,GAAG,IAU7D,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,SAAS,GAAI,MAAM,IAAI,KAAG,MAAiC,CAAA;AAExE;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAI,MAAM,IAAI,EAAE,eAAe,MAAM,KAAG,MAAM,GAAG,IAI9E,CAAA"}
@@ -20,15 +20,22 @@ export const PLAN_LIMITS = {
20
20
  can_cli_sync: false,
21
21
  can_cli_update: false,
22
22
  can_cross_machine_sync: false,
23
+ // Free can author *one* private skill — enough to try the editor,
24
+ // not enough to fill the catalogue. Public publishing + versioning
25
+ // stay locked behind Pro because that's where Skill Intelligence
26
+ // (token audit + conflict detection) starts paying for itself.
23
27
  can_authoring_public: false,
24
- can_authoring_private: false,
25
- authoring_max_skills: 0,
28
+ can_authoring_private: true,
29
+ authoring_max_skills: 1,
26
30
  can_versioning: false,
27
31
  can_ai_description: false,
28
32
  can_pull_requests: false,
29
- tester_credits_monthly: 0,
33
+ // Free gets a single Tester run / month — one taste of the agent
34
+ // sandbox so the upgrade prompt isn't an abstract "you've never
35
+ // seen this" wall. 1 run = 5 credits = ~$0.02 LLM cost to us.
36
+ tester_credits_monthly: 5,
30
37
  tester_cost_per_run: 5,
31
- tester_cooldown_seconds: 0,
38
+ tester_cooldown_seconds: 600,
32
39
  can_byo_tester_key: false,
33
40
  can_intelligence: false,
34
41
  intelligence_indexed_max: 0,
@@ -43,6 +50,10 @@ export const PLAN_LIMITS = {
43
50
  has_priority_badge: false,
44
51
  has_priority_support: false,
45
52
  has_early_access: false,
53
+ runs_max_active: 0,
54
+ runs_min_frequency: 'none',
55
+ runs_max_timeout_seconds: 0,
56
+ runs_max_log_bytes: 0,
46
57
  },
47
58
  // ── Pro (internally `explorer`) ───────────────────────────────────
48
59
  explorer: {
@@ -89,6 +100,10 @@ export const PLAN_LIMITS = {
89
100
  has_priority_badge: false,
90
101
  has_priority_support: false,
91
102
  has_early_access: false,
103
+ runs_max_active: 3,
104
+ runs_min_frequency: 'daily',
105
+ runs_max_timeout_seconds: 60,
106
+ runs_max_log_bytes: 65_536,
92
107
  },
93
108
  // ── Max (internally `creator`) ────────────────────────────────────
94
109
  creator: {
@@ -133,6 +148,10 @@ export const PLAN_LIMITS = {
133
148
  has_priority_badge: true,
134
149
  has_priority_support: true,
135
150
  has_early_access: true,
151
+ runs_max_active: 25,
152
+ runs_min_frequency: 'custom',
153
+ runs_max_timeout_seconds: 300,
154
+ runs_max_log_bytes: 262_144,
136
155
  },
137
156
  };
138
157
  export const PLAN_RANK = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prave/shared",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"