shippilot 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/LICENSE +21 -0
  3. package/README.md +241 -0
  4. package/dist/auth/prepareCodexHome.d.ts +7 -0
  5. package/dist/auth/prepareCodexHome.js +52 -0
  6. package/dist/auth/prepareCodexHome.js.map +1 -0
  7. package/dist/auth/validateAuth.d.ts +2 -0
  8. package/dist/auth/validateAuth.js +19 -0
  9. package/dist/auth/validateAuth.js.map +1 -0
  10. package/dist/cases/parseCase.d.ts +13 -0
  11. package/dist/cases/parseCase.js +21 -0
  12. package/dist/cases/parseCase.js.map +1 -0
  13. package/dist/cases/resolveEnv.d.ts +6 -0
  14. package/dist/cases/resolveEnv.js +19 -0
  15. package/dist/cases/resolveEnv.js.map +1 -0
  16. package/dist/cli.d.ts +2 -0
  17. package/dist/cli.js +219 -0
  18. package/dist/cli.js.map +1 -0
  19. package/dist/codex/outputSchema.d.ts +72 -0
  20. package/dist/codex/outputSchema.js +48 -0
  21. package/dist/codex/outputSchema.js.map +1 -0
  22. package/dist/codex/promptBuilder.d.ts +8 -0
  23. package/dist/codex/promptBuilder.js +48 -0
  24. package/dist/codex/promptBuilder.js.map +1 -0
  25. package/dist/codex/runWithSdk.d.ts +11 -0
  26. package/dist/codex/runWithSdk.js +396 -0
  27. package/dist/codex/runWithSdk.js.map +1 -0
  28. package/dist/config/loadConfig.d.ts +2 -0
  29. package/dist/config/loadConfig.js +13 -0
  30. package/dist/config/loadConfig.js.map +1 -0
  31. package/dist/config/schema.d.ts +43 -0
  32. package/dist/config/schema.js +71 -0
  33. package/dist/config/schema.js.map +1 -0
  34. package/dist/exitCodes.d.ts +7 -0
  35. package/dist/exitCodes.js +7 -0
  36. package/dist/exitCodes.js.map +1 -0
  37. package/dist/ios/doctorXcode.d.ts +3 -0
  38. package/dist/ios/doctorXcode.js +33 -0
  39. package/dist/ios/doctorXcode.js.map +1 -0
  40. package/dist/ios/xcodebuildmcp.d.ts +15 -0
  41. package/dist/ios/xcodebuildmcp.js +95 -0
  42. package/dist/ios/xcodebuildmcp.js.map +1 -0
  43. package/dist/reports/jsonReport.d.ts +11 -0
  44. package/dist/reports/jsonReport.js +27 -0
  45. package/dist/reports/jsonReport.js.map +1 -0
  46. package/dist/reports/junitReport.d.ts +4 -0
  47. package/dist/reports/junitReport.js +39 -0
  48. package/dist/reports/junitReport.js.map +1 -0
  49. package/dist/reports/markdownReport.d.ts +4 -0
  50. package/dist/reports/markdownReport.js +46 -0
  51. package/dist/reports/markdownReport.js.map +1 -0
  52. package/dist/security/redact.d.ts +5 -0
  53. package/dist/security/redact.js +12 -0
  54. package/dist/security/redact.js.map +1 -0
  55. package/docs/auth.md +47 -0
  56. package/docs/bitrise.md +22 -0
  57. package/docs/github-actions.md +47 -0
  58. package/docs/personal-chatgpt-subscription.md +31 -0
  59. package/docs/plan.md +55 -0
  60. package/docs/release.md +87 -0
  61. package/docs/security.md +39 -0
  62. package/examples/bitrise/shippilot.sh +6 -0
  63. package/examples/github-actions/shippilot.yml +39 -0
  64. package/examples/qa/login.md +15 -0
  65. package/examples/shippilot.yml +24 -0
  66. package/package.json +51 -0
@@ -0,0 +1,13 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ import YAML from "yaml";
4
+ import { configSchema } from "./schema.js";
5
+ export function loadConfig(configPath = "shippilot.yml", cwd = process.cwd()) {
6
+ const absolutePath = path.resolve(cwd, configPath);
7
+ if (!existsSync(absolutePath)) {
8
+ throw new Error(`Config file not found: ${absolutePath}`);
9
+ }
10
+ const parsed = YAML.parse(readFileSync(absolutePath, "utf8"));
11
+ return configSchema.parse(parsed);
12
+ }
13
+ //# sourceMappingURL=loadConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loadConfig.js","sourceRoot":"","sources":["../../src/config/loadConfig.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,YAAY,EAAwB,MAAM,aAAa,CAAC;AAEjE,MAAM,UAAU,UAAU,CAAC,UAAU,GAAG,eAAe,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IAC1E,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IACnD,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,0BAA0B,YAAY,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC;IAC9D,OAAO,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;AACpC,CAAC"}
@@ -0,0 +1,43 @@
1
+ import { z } from "zod";
2
+ export declare const authModes: readonly ["api_key", "access_token", "chatgpt_hosted_experimental"];
3
+ export declare const failModes: readonly ["failed_or_blocked", "never"];
4
+ export declare const configSchema: z.ZodObject<{
5
+ codex: z.ZodDefault<z.ZodObject<{
6
+ engine: z.ZodDefault<z.ZodLiteral<"sdk">>;
7
+ auth: z.ZodDefault<z.ZodEnum<{
8
+ api_key: "api_key";
9
+ access_token: "access_token";
10
+ chatgpt_hosted_experimental: "chatgpt_hosted_experimental";
11
+ }>>;
12
+ model: z.ZodDefault<z.ZodString>;
13
+ sandbox: z.ZodDefault<z.ZodEnum<{
14
+ "read-only": "read-only";
15
+ "workspace-write": "workspace-write";
16
+ "danger-full-access": "danger-full-access";
17
+ }>>;
18
+ fail_on: z.ZodDefault<z.ZodEnum<{
19
+ failed_or_blocked: "failed_or_blocked";
20
+ never: "never";
21
+ }>>;
22
+ verbose: z.ZodDefault<z.ZodBoolean>;
23
+ allow_experimental_personal_hosted_auth: z.ZodDefault<z.ZodBoolean>;
24
+ }, z.core.$strip>>;
25
+ ios: z.ZodObject<{
26
+ project: z.ZodNullable<z.ZodOptional<z.ZodString>>;
27
+ workspace: z.ZodNullable<z.ZodOptional<z.ZodString>>;
28
+ scheme: z.ZodString;
29
+ bundle_id: z.ZodNullable<z.ZodOptional<z.ZodString>>;
30
+ simulator: z.ZodDefault<z.ZodString>;
31
+ backend: z.ZodDefault<z.ZodLiteral<"xcodebuildmcp">>;
32
+ configuration: z.ZodDefault<z.ZodString>;
33
+ }, z.core.$strip>;
34
+ reports: z.ZodDefault<z.ZodObject<{
35
+ output_dir: z.ZodDefault<z.ZodString>;
36
+ markdown: z.ZodDefault<z.ZodBoolean>;
37
+ json: z.ZodDefault<z.ZodBoolean>;
38
+ junit: z.ZodDefault<z.ZodBoolean>;
39
+ screenshots: z.ZodDefault<z.ZodBoolean>;
40
+ logs: z.ZodDefault<z.ZodBoolean>;
41
+ }, z.core.$strip>>;
42
+ }, z.core.$strip>;
43
+ export type ShipPilotConfig = z.infer<typeof configSchema>;
@@ -0,0 +1,71 @@
1
+ import { z } from "zod";
2
+ export const authModes = ["api_key", "access_token", "chatgpt_hosted_experimental"];
3
+ export const failModes = ["failed_or_blocked", "never"];
4
+ export const configSchema = z
5
+ .object({
6
+ codex: z
7
+ .object({
8
+ engine: z.literal("sdk").default("sdk"),
9
+ auth: z.enum(authModes).default("api_key"),
10
+ model: z.string().default("default"),
11
+ sandbox: z.enum(["read-only", "workspace-write", "danger-full-access"]).default("danger-full-access"),
12
+ fail_on: z.enum(failModes).default("failed_or_blocked"),
13
+ verbose: z.boolean().default(false),
14
+ allow_experimental_personal_hosted_auth: z.boolean().default(false),
15
+ })
16
+ .default({
17
+ engine: "sdk",
18
+ auth: "api_key",
19
+ model: "default",
20
+ sandbox: "danger-full-access",
21
+ fail_on: "failed_or_blocked",
22
+ verbose: false,
23
+ allow_experimental_personal_hosted_auth: false,
24
+ }),
25
+ ios: z.object({
26
+ project: z.string().optional().nullable(),
27
+ workspace: z.string().optional().nullable(),
28
+ scheme: z.string().min(1),
29
+ bundle_id: z.string().optional().nullable(),
30
+ simulator: z.string().default("iPhone 17 Pro"),
31
+ backend: z.literal("xcodebuildmcp").default("xcodebuildmcp"),
32
+ configuration: z.string().default("Debug"),
33
+ }),
34
+ reports: z
35
+ .object({
36
+ output_dir: z.string().default(".shippilot"),
37
+ markdown: z.boolean().default(true),
38
+ json: z.boolean().default(true),
39
+ junit: z.boolean().default(true),
40
+ screenshots: z.boolean().default(true),
41
+ logs: z.boolean().default(true),
42
+ })
43
+ .default({
44
+ output_dir: ".shippilot",
45
+ markdown: true,
46
+ json: true,
47
+ junit: true,
48
+ screenshots: true,
49
+ logs: true,
50
+ }),
51
+ })
52
+ .superRefine((value, context) => {
53
+ const hasProject = Boolean(value.ios.project);
54
+ const hasWorkspace = Boolean(value.ios.workspace);
55
+ if (hasProject === hasWorkspace) {
56
+ context.addIssue({
57
+ code: "custom",
58
+ message: "Configure exactly one of ios.project or ios.workspace.",
59
+ path: ["ios"],
60
+ });
61
+ }
62
+ if (value.codex.auth === "chatgpt_hosted_experimental" &&
63
+ !value.codex.allow_experimental_personal_hosted_auth) {
64
+ context.addIssue({
65
+ code: "custom",
66
+ message: "chatgpt_hosted_experimental requires codex.allow_experimental_personal_hosted_auth: true.",
67
+ path: ["codex", "allow_experimental_personal_hosted_auth"],
68
+ });
69
+ }
70
+ });
71
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export declare const ExitCodes: {
2
+ readonly success: 0;
3
+ readonly failed: 1;
4
+ readonly setupError: 2;
5
+ readonly blocked: 3;
6
+ };
7
+ export type ExitCode = (typeof ExitCodes)[keyof typeof ExitCodes];
@@ -0,0 +1,7 @@
1
+ export const ExitCodes = {
2
+ success: 0,
3
+ failed: 1,
4
+ setupError: 2,
5
+ blocked: 3,
6
+ };
7
+ //# sourceMappingURL=exitCodes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exitCodes.js","sourceRoot":"","sources":["../src/exitCodes.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,OAAO,EAAE,CAAC;IACV,MAAM,EAAE,CAAC;IACT,UAAU,EAAE,CAAC;IACb,OAAO,EAAE,CAAC;CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { ShipPilotConfig } from "../config/schema.js";
2
+ import { type CheckResult } from "./xcodebuildmcp.js";
3
+ export declare function doctorXcode(config: ShipPilotConfig, cwd?: string): CheckResult[];
@@ -0,0 +1,33 @@
1
+ import { existsSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { resolveXcodeBuildMcpCommand, runCommand } from "./xcodebuildmcp.js";
4
+ export function doctorXcode(config, cwd = process.cwd()) {
5
+ const projectPath = config.ios.project ?? config.ios.workspace;
6
+ let xcodeBuildMcp = null;
7
+ let xcodeBuildMcpResolution = null;
8
+ try {
9
+ xcodeBuildMcp = resolveXcodeBuildMcpCommand();
10
+ }
11
+ catch (error) {
12
+ xcodeBuildMcpResolution = {
13
+ name: "bundled xcodebuildmcp",
14
+ ok: false,
15
+ detail: error instanceof Error ? error.message : String(error),
16
+ };
17
+ }
18
+ const checks = [runCommand("xcodebuild", ["-version"], cwd)];
19
+ if (xcodeBuildMcpResolution) {
20
+ checks.push(xcodeBuildMcpResolution);
21
+ }
22
+ else if (xcodeBuildMcp) {
23
+ checks.push(runCommand(xcodeBuildMcp, ["--help"], cwd, "bundled xcodebuildmcp"));
24
+ checks.push(runCommand(xcodeBuildMcp, ["simulator", "list"], cwd, "bundled xcodebuildmcp simulator list"));
25
+ }
26
+ checks.push({
27
+ name: "iOS project/workspace",
28
+ ok: Boolean(projectPath && existsSync(path.resolve(cwd, projectPath))),
29
+ detail: projectPath ? path.resolve(cwd, projectPath) : "Missing ios.project or ios.workspace",
30
+ });
31
+ return checks;
32
+ }
33
+ //# sourceMappingURL=doctorXcode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctorXcode.js","sourceRoot":"","sources":["../../src/ios/doctorXcode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,2BAA2B,EAAE,UAAU,EAAoB,MAAM,oBAAoB,CAAC;AAE/F,MAAM,UAAU,WAAW,CAAC,MAAuB,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IACtE,MAAM,WAAW,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC;IAC/D,IAAI,aAAa,GAAkB,IAAI,CAAC;IACxC,IAAI,uBAAuB,GAAuB,IAAI,CAAC;IAEvD,IAAI,CAAC;QACH,aAAa,GAAG,2BAA2B,EAAE,CAAC;IAChD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,uBAAuB,GAAG;YACxB,IAAI,EAAE,uBAAuB;YAC7B,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;SAC/D,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAkB,CAAC,UAAU,CAAC,YAAY,EAAE,CAAC,UAAU,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;IAE5E,IAAI,uBAAuB,EAAE,CAAC;QAC5B,MAAM,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;IACvC,CAAC;SAAM,IAAI,aAAa,EAAE,CAAC;QACzB,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,uBAAuB,CAAC,CAAC,CAAC;QACjF,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC,WAAW,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,sCAAsC,CAAC,CAAC,CAAC;IAC7G,CAAC;IAED,MAAM,CAAC,IAAI,CAAC;QACV,IAAI,EAAE,uBAAuB;QAC7B,EAAE,EAAE,OAAO,CAAC,WAAW,IAAI,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC,CAAC;QACtE,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,sCAAsC;KAC9F,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { ShipPilotConfig } from "../config/schema.js";
2
+ export type CheckResult = {
3
+ name: string;
4
+ ok: boolean;
5
+ detail: string;
6
+ };
7
+ export declare function resolveXcodeBuildMcpCommand(): string;
8
+ export declare function runCommand(command: string, args: string[], cwd?: string, name?: string): CheckResult;
9
+ export declare function buildArgs(config: ShipPilotConfig, simulatorId?: string): string[];
10
+ export declare function bootArgs(config: ShipPilotConfig, simulatorId?: string): string[];
11
+ export declare function getAppPathArgs(config: ShipPilotConfig, simulatorId?: string): string[];
12
+ export declare function getBundleIdArgs(appPath: string): string[];
13
+ export declare function installArgs(config: ShipPilotConfig, appPath: string, simulatorId?: string): string[];
14
+ export declare function launchArgs(config: ShipPilotConfig, bundleId: string, simulatorId?: string): string[];
15
+ export declare function projectFileExists(config: ShipPilotConfig, cwd?: string): boolean;
@@ -0,0 +1,95 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { createRequire } from "node:module";
3
+ import { existsSync, readFileSync } from "node:fs";
4
+ import { fileURLToPath } from "node:url";
5
+ import path from "node:path";
6
+ const bundledXcodeBuildMcpMissingMessage = "Bundled xcodebuildmcp was not found. Run npm install in the ShipPilot package.";
7
+ const require = createRequire(import.meta.url);
8
+ function packageRoot() {
9
+ return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
10
+ }
11
+ export function resolveXcodeBuildMcpCommand() {
12
+ const binName = process.platform === "win32" ? "xcodebuildmcp.cmd" : "xcodebuildmcp";
13
+ const localBin = path.join(packageRoot(), "node_modules", ".bin", binName);
14
+ if (existsSync(localBin))
15
+ return localBin;
16
+ try {
17
+ const packageJsonPath = require.resolve("xcodebuildmcp/package.json");
18
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
19
+ const binPath = typeof packageJson.bin === "string" ? packageJson.bin : packageJson.bin?.xcodebuildmcp;
20
+ if (binPath) {
21
+ const command = path.resolve(path.dirname(packageJsonPath), binPath);
22
+ if (existsSync(command))
23
+ return command;
24
+ }
25
+ }
26
+ catch {
27
+ // Fall through to the setup error below. ShipPilot should not require a global xcodebuildmcp.
28
+ }
29
+ throw new Error(bundledXcodeBuildMcpMissingMessage);
30
+ }
31
+ export function runCommand(command, args, cwd = process.cwd(), name) {
32
+ const result = spawnSync(command, args, { cwd, encoding: "utf8" });
33
+ return {
34
+ name: name ?? [command, ...args].join(" "),
35
+ ok: result.status === 0,
36
+ detail: (result.stdout || result.stderr || "").trim(),
37
+ };
38
+ }
39
+ function addProjectOrWorkspace(args, config) {
40
+ if (config.ios.project) {
41
+ args.push("--project-path", config.ios.project);
42
+ }
43
+ else if (config.ios.workspace) {
44
+ args.push("--workspace-path", config.ios.workspace);
45
+ }
46
+ return args;
47
+ }
48
+ function addSimulator(args, config, simulatorId) {
49
+ if (simulatorId) {
50
+ args.push("--simulator-id", simulatorId);
51
+ }
52
+ else {
53
+ args.push("--simulator-name", config.ios.simulator);
54
+ }
55
+ return args;
56
+ }
57
+ export function buildArgs(config, simulatorId) {
58
+ return addProjectOrWorkspace(addSimulator([
59
+ "simulator",
60
+ "build",
61
+ "--scheme",
62
+ config.ios.scheme,
63
+ "--configuration",
64
+ config.ios.configuration,
65
+ ], config, simulatorId), config);
66
+ }
67
+ export function bootArgs(config, simulatorId) {
68
+ return addSimulator(["simulator", "boot"], config, simulatorId);
69
+ }
70
+ export function getAppPathArgs(config, simulatorId) {
71
+ return addProjectOrWorkspace(addSimulator([
72
+ "simulator",
73
+ "get-app-path",
74
+ "--scheme",
75
+ config.ios.scheme,
76
+ "--platform",
77
+ "iOS Simulator",
78
+ "--configuration",
79
+ config.ios.configuration,
80
+ ], config, simulatorId), config);
81
+ }
82
+ export function getBundleIdArgs(appPath) {
83
+ return ["simulator", "get-app-bundle-id", "--app-path", appPath];
84
+ }
85
+ export function installArgs(config, appPath, simulatorId) {
86
+ return addSimulator(["simulator", "install", "--app-path", appPath], config, simulatorId);
87
+ }
88
+ export function launchArgs(config, bundleId, simulatorId) {
89
+ return addSimulator(["simulator", "launch-app", "--bundle-id", bundleId], config, simulatorId);
90
+ }
91
+ export function projectFileExists(config, cwd = process.cwd()) {
92
+ const projectPath = config.ios.project ?? config.ios.workspace;
93
+ return Boolean(projectPath && path.resolve(cwd, projectPath));
94
+ }
95
+ //# sourceMappingURL=xcodebuildmcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"xcodebuildmcp.js","sourceRoot":"","sources":["../../src/ios/xcodebuildmcp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { ShipPilotConfig } from "../config/schema.js";
2
+ import type { CaseRunRecord } from "../codex/runWithSdk.js";
3
+ export type RunReport = {
4
+ status: "passed" | "failed" | "blocked";
5
+ started_at: string;
6
+ completed_at: string;
7
+ cases: CaseRunRecord[];
8
+ };
9
+ export declare function summarizeStatus(cases: CaseRunRecord[]): RunReport["status"];
10
+ export declare function writeJsonReport(config: ShipPilotConfig, cases: CaseRunRecord[], startedAt: string, cwd?: string): RunReport;
11
+ export declare function readJsonReport(runPath: string, cwd?: string): RunReport;
@@ -0,0 +1,27 @@
1
+ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ export function summarizeStatus(cases) {
4
+ if (cases.some((entry) => entry.result.status === "failed")) {
5
+ return "failed";
6
+ }
7
+ if (cases.some((entry) => entry.result.status === "blocked")) {
8
+ return "blocked";
9
+ }
10
+ return "passed";
11
+ }
12
+ export function writeJsonReport(config, cases, startedAt, cwd = process.cwd()) {
13
+ const outputDir = path.resolve(cwd, config.reports.output_dir);
14
+ mkdirSync(outputDir, { recursive: true });
15
+ const report = {
16
+ status: summarizeStatus(cases),
17
+ started_at: startedAt,
18
+ completed_at: new Date().toISOString(),
19
+ cases,
20
+ };
21
+ writeFileSync(path.join(outputDir, "run.json"), `${JSON.stringify(report, null, 2)}\n`);
22
+ return report;
23
+ }
24
+ export function readJsonReport(runPath, cwd = process.cwd()) {
25
+ return JSON.parse(readFileSync(path.resolve(cwd, runPath), "utf8"));
26
+ }
27
+ //# sourceMappingURL=jsonReport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsonReport.js","sourceRoot":"","sources":["../../src/reports/jsonReport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACjE,OAAO,IAAI,MAAM,WAAW,CAAC;AAW7B,MAAM,UAAU,eAAe,CAAC,KAAsB;IACpD,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,EAAE,CAAC;QAC5D,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,KAAK,SAAS,CAAC,EAAE,CAAC;QAC7D,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,MAAuB,EACvB,KAAsB,EACtB,SAAiB,EACjB,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IAEnB,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC/D,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE1C,MAAM,MAAM,GAAc;QACxB,MAAM,EAAE,eAAe,CAAC,KAAK,CAAC;QAC9B,UAAU,EAAE,SAAS;QACrB,YAAY,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACtC,KAAK;KACN,CAAC;IAEF,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IACxF,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAe,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IACjE,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,CAAc,CAAC;AACnF,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { ShipPilotConfig } from "../config/schema.js";
2
+ import type { RunReport } from "./jsonReport.js";
3
+ export declare function renderJunitReport(report: RunReport): string;
4
+ export declare function writeJunitReport(config: ShipPilotConfig, report: RunReport, cwd?: string): void;
@@ -0,0 +1,39 @@
1
+ import { mkdirSync, writeFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ function escapeXml(value) {
4
+ return value
5
+ .replaceAll("&", "&amp;")
6
+ .replaceAll("<", "&lt;")
7
+ .replaceAll(">", "&gt;")
8
+ .replaceAll('"', "&quot;")
9
+ .replaceAll("'", "&apos;");
10
+ }
11
+ export function renderJunitReport(report) {
12
+ const failures = report.cases.filter((entry) => entry.result.status === "failed").length;
13
+ const errors = report.cases.filter((entry) => entry.result.status === "blocked").length;
14
+ const testcases = report.cases
15
+ .map((entry) => {
16
+ const result = entry.result;
17
+ const name = escapeXml(`${result.case_id}: ${result.title}`);
18
+ const body = result.status === "failed"
19
+ ? `<failure message="${escapeXml(result.failure_reason ?? result.summary)}">${escapeXml(result.observed)}</failure>`
20
+ : result.status === "blocked"
21
+ ? `<error message="${escapeXml(result.blocked_reason ?? result.summary)}">${escapeXml(result.observed)}</error>`
22
+ : "";
23
+ return ` <testcase classname="ShipPilot" name="${name}">${body}</testcase>`;
24
+ })
25
+ .join("\n");
26
+ return [
27
+ '<?xml version="1.0" encoding="UTF-8"?>',
28
+ `<testsuite name="ShipPilot" tests="${report.cases.length}" failures="${failures}" errors="${errors}">`,
29
+ testcases,
30
+ "</testsuite>",
31
+ "",
32
+ ].join("\n");
33
+ }
34
+ export function writeJunitReport(config, report, cwd = process.cwd()) {
35
+ const outputDir = path.resolve(cwd, config.reports.output_dir);
36
+ mkdirSync(outputDir, { recursive: true });
37
+ writeFileSync(path.join(outputDir, "junit.xml"), renderJunitReport(report));
38
+ }
39
+ //# sourceMappingURL=junitReport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"junitReport.js","sourceRoot":"","sources":["../../src/reports/junitReport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,IAAI,MAAM,WAAW,CAAC;AAI7B,SAAS,SAAS,CAAC,KAAa;IAC9B,OAAO,KAAK;SACT,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC;SACxB,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC;SACvB,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC;SACvB,UAAU,CAAC,GAAG,EAAE,QAAQ,CAAC;SACzB,UAAU,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAiB;IACjD,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,MAAM,CAAC;IACzF,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,MAAM,CAAC;IACxF,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK;SAC3B,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACb,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;QAC5B,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,MAAM,CAAC,OAAO,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;QAC7D,MAAM,IAAI,GACR,MAAM,CAAC,MAAM,KAAK,QAAQ;YACxB,CAAC,CAAC,qBAAqB,SAAS,CAAC,MAAM,CAAC,cAAc,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,SAAS,CACnF,MAAM,CAAC,QAAQ,CAChB,YAAY;YACf,CAAC,CAAC,MAAM,CAAC,MAAM,KAAK,SAAS;gBAC3B,CAAC,CAAC,mBAAmB,SAAS,CAAC,MAAM,CAAC,cAAc,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,SAAS,CACjF,MAAM,CAAC,QAAQ,CAChB,UAAU;gBACb,CAAC,CAAC,EAAE,CAAC;QACX,OAAO,6CAA6C,IAAI,KAAK,IAAI,aAAa,CAAC;IACjF,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,OAAO;QACL,wCAAwC;QACxC,sCAAsC,MAAM,CAAC,KAAK,CAAC,MAAM,eAAe,QAAQ,aAAa,MAAM,IAAI;QACvG,SAAS;QACT,cAAc;QACd,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,MAAuB,EAAE,MAAiB,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IAC9F,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC/D,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,EAAE,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC;AAC9E,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { ShipPilotConfig } from "../config/schema.js";
2
+ import type { RunReport } from "./jsonReport.js";
3
+ export declare function renderMarkdownReport(report: RunReport): string;
4
+ export declare function writeMarkdownReport(config: ShipPilotConfig, report: RunReport, cwd?: string): void;
@@ -0,0 +1,46 @@
1
+ import { mkdirSync, writeFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ function statusIcon(status) {
4
+ if (status === "passed")
5
+ return "PASS";
6
+ if (status === "failed")
7
+ return "FAIL";
8
+ return "BLOCKED";
9
+ }
10
+ export function renderMarkdownReport(report) {
11
+ const lines = [
12
+ "# ShipPilot Report",
13
+ "",
14
+ `Status: ${statusIcon(report.status)}`,
15
+ `Started: ${report.started_at}`,
16
+ `Completed: ${report.completed_at}`,
17
+ "",
18
+ "## Cases",
19
+ "",
20
+ ];
21
+ for (const entry of report.cases) {
22
+ const result = entry.result;
23
+ lines.push(`### ${statusIcon(result.status)} ${result.case_id}: ${result.title}`, "", result.summary, "", `Expected: ${result.expected}`, "", `Observed: ${result.observed}`, "", `Confidence: ${result.confidence}`, "");
24
+ if (result.failure_reason) {
25
+ lines.push(`Failure reason: ${result.failure_reason}`, "");
26
+ }
27
+ if (result.blocked_reason) {
28
+ lines.push(`Blocked reason: ${result.blocked_reason}`, "");
29
+ }
30
+ if (result.executed_steps.length > 0) {
31
+ lines.push("Executed steps:", "");
32
+ lines.push(...result.executed_steps.map((step) => `- ${step}`), "");
33
+ }
34
+ if (result.evidence.length > 0) {
35
+ lines.push("Evidence:", "");
36
+ lines.push(...result.evidence.map((item) => `- ${item}`), "");
37
+ }
38
+ }
39
+ return `${lines.join("\n").trim()}\n`;
40
+ }
41
+ export function writeMarkdownReport(config, report, cwd = process.cwd()) {
42
+ const outputDir = path.resolve(cwd, config.reports.output_dir);
43
+ mkdirSync(outputDir, { recursive: true });
44
+ writeFileSync(path.join(outputDir, "report.md"), renderMarkdownReport(report));
45
+ }
46
+ //# sourceMappingURL=markdownReport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdownReport.js","sourceRoot":"","sources":["../../src/reports/markdownReport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,IAAI,MAAM,WAAW,CAAC;AAI7B,SAAS,UAAU,CAAC,MAAc;IAChC,IAAI,MAAM,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC;IACvC,IAAI,MAAM,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC;IACvC,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,MAAiB;IACpD,MAAM,KAAK,GAAG;QACZ,oBAAoB;QACpB,EAAE;QACF,WAAW,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE;QACtC,YAAY,MAAM,CAAC,UAAU,EAAE;QAC/B,cAAc,MAAM,CAAC,YAAY,EAAE;QACnC,EAAE;QACF,UAAU;QACV,EAAE;KACH,CAAC;IAEF,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjC,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;QAC5B,KAAK,CAAC,IAAI,CACR,OAAO,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,KAAK,MAAM,CAAC,KAAK,EAAE,EACrE,EAAE,EACF,MAAM,CAAC,OAAO,EACd,EAAE,EACF,aAAa,MAAM,CAAC,QAAQ,EAAE,EAC9B,EAAE,EACF,aAAa,MAAM,CAAC,QAAQ,EAAE,EAC9B,EAAE,EACF,eAAe,MAAM,CAAC,UAAU,EAAE,EAClC,EAAE,CACH,CAAC;QAEF,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;YAC1B,KAAK,CAAC,IAAI,CAAC,mBAAmB,MAAM,CAAC,cAAc,EAAE,EAAE,EAAE,CAAC,CAAC;QAC7D,CAAC;QACD,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;YAC1B,KAAK,CAAC,IAAI,CAAC,mBAAmB,MAAM,CAAC,cAAc,EAAE,EAAE,EAAE,CAAC,CAAC;QAC7D,CAAC;QACD,IAAI,MAAM,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrC,KAAK,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC;YAClC,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QACtE,CAAC;QACD,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC/B,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;YAC5B,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;IAED,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,MAAuB,EACvB,MAAiB,EACjB,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IAEnB,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC/D,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,EAAE,oBAAoB,CAAC,MAAM,CAAC,CAAC,CAAC;AACjF,CAAC"}
@@ -0,0 +1,5 @@
1
+ export type Redactor = {
2
+ redact: (value: string) => string;
3
+ secrets: string[];
4
+ };
5
+ export declare function createRedactor(values: Array<string | undefined | null>): Redactor;
@@ -0,0 +1,12 @@
1
+ export function createRedactor(values) {
2
+ const secrets = values
3
+ .filter((value) => typeof value === "string" && value.length > 0)
4
+ .sort((a, b) => b.length - a.length);
5
+ return {
6
+ secrets,
7
+ redact(value) {
8
+ return secrets.reduce((current, secret) => current.split(secret).join("[REDACTED]"), value);
9
+ },
10
+ };
11
+ }
12
+ //# sourceMappingURL=redact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redact.js","sourceRoot":"","sources":["../../src/security/redact.ts"],"names":[],"mappings":"AAKA,MAAM,UAAU,cAAc,CAAC,MAAwC;IACrE,MAAM,OAAO,GAAG,MAAM;SACnB,MAAM,CAAC,CAAC,KAAK,EAAmB,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;SACjF,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;IAEvC,OAAO;QACL,OAAO;QACP,MAAM,CAAC,KAAa;YAClB,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,KAAK,CAAC,CAAC;QAC9F,CAAC;KACF,CAAC;AACJ,CAAC"}
package/docs/auth.md ADDED
@@ -0,0 +1,47 @@
1
+ # Auth
2
+
3
+ ShipPilot supports three auth modes.
4
+
5
+ ## `api_key`
6
+
7
+ Use `OPENAI_API_KEY`.
8
+
9
+ This is the recommended default for GitHub-hosted runners, Bitrise, and open-source projects. It uses OpenAI Platform/API billing.
10
+
11
+ ```yaml
12
+ codex:
13
+ auth: api_key
14
+ ```
15
+
16
+ ## `access_token`
17
+
18
+ Use `CODEX_ACCESS_TOKEN`.
19
+
20
+ This is intended for trusted Business/Enterprise automation where teams want workspace identity and governance.
21
+
22
+ ```yaml
23
+ codex:
24
+ auth: access_token
25
+ ```
26
+
27
+ ShipPilot bootstraps Codex with:
28
+
29
+ ```bash
30
+ printenv CODEX_ACCESS_TOKEN | codex login --with-access-token
31
+ ```
32
+
33
+ ## `chatgpt_hosted_experimental`
34
+
35
+ This mode attempts personal ChatGPT subscription auth on GitHub-hosted runners by restoring a pre-authenticated Codex home from a secret.
36
+
37
+ ```yaml
38
+ codex:
39
+ auth: chatgpt_hosted_experimental
40
+ allow_experimental_personal_hosted_auth: true
41
+ ```
42
+
43
+ Required secret:
44
+
45
+ - `CODEX_HOME_TGZ_BASE64`
46
+
47
+ This is fragile, sensitive, and not recommended for public fork PR workflows.
@@ -0,0 +1,22 @@
1
+ # Bitrise
2
+
3
+ Add ShipPilot as a Script Step after dependency setup.
4
+
5
+ ```bash
6
+ #!/usr/bin/env bash
7
+ set -euo pipefail
8
+
9
+ npm install -g shippilot
10
+ shippilot doctor
11
+ shippilot run --case qa/login.md
12
+ ```
13
+
14
+ Requirements:
15
+
16
+ - macOS stack with Xcode.
17
+ - ShipPilot installs its bundled XcodeBuildMCP dependency; no separate XcodeBuildMCP step is needed.
18
+ - App project dependencies installed before the step runs.
19
+ - `OPENAI_API_KEY` or `CODEX_ACCESS_TOKEN` as Bitrise Secrets.
20
+ - App test credentials such as `TEST_EMAIL` and `TEST_PASSWORD`.
21
+
22
+ Upload `.shippilot/` as Bitrise artifacts so failed QA runs still leave evidence.
@@ -0,0 +1,47 @@
1
+ # GitHub Actions
2
+
3
+ Use macOS runners because iOS simulator testing requires Xcode. ShipPilot bundles XcodeBuildMCP through its npm dependencies, so workflows do not install it separately.
4
+
5
+ ```yaml
6
+ name: ShipPilot QA
7
+
8
+ on:
9
+ workflow_dispatch:
10
+ release:
11
+ types: [published]
12
+
13
+ jobs:
14
+ shippilot:
15
+ runs-on: macos-15
16
+ permissions:
17
+ contents: read
18
+
19
+ steps:
20
+ - uses: actions/checkout@v5
21
+ with:
22
+ persist-credentials: false
23
+
24
+ - uses: actions/setup-node@v4
25
+ with:
26
+ node-version: 22
27
+
28
+ - name: Run ShipPilot
29
+ env:
30
+ OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
31
+ CODEX_ACCESS_TOKEN: ${{ secrets.CODEX_ACCESS_TOKEN }}
32
+ CODEX_HOME_TGZ_BASE64: ${{ secrets.CODEX_HOME_TGZ_BASE64 }}
33
+ TEST_EMAIL: ${{ secrets.TEST_EMAIL }}
34
+ TEST_PASSWORD: ${{ secrets.TEST_PASSWORD }}
35
+ run: |
36
+ npx shippilot doctor
37
+ npx shippilot run --case qa/login.md
38
+
39
+ - name: Upload ShipPilot report
40
+ if: always()
41
+ uses: actions/upload-artifact@v4
42
+ with:
43
+ name: shippilot-report
44
+ path: .shippilot/
45
+ ```
46
+
47
+ For open-source projects, prefer `workflow_dispatch`, releases, schedules, or maintainer-approved labels. Do not expose secrets to arbitrary fork PRs.