apphx 1.1.0 → 2.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 (121) hide show
  1. package/README.md +50 -182
  2. package/benchmarks/peer-context-worker.mjs +186 -0
  3. package/benchmarks/peer-context.mjs +150 -0
  4. package/dist/apple-types.js.map +1 -1
  5. package/dist/cli.js +370 -118
  6. package/dist/cli.js.map +1 -1
  7. package/dist/config.js +42 -4
  8. package/dist/config.js.map +1 -1
  9. package/dist/dependencies.js +455 -16
  10. package/dist/dependencies.js.map +1 -1
  11. package/dist/export-options.js +44 -2
  12. package/dist/export-options.js.map +1 -1
  13. package/dist/flutter.js +13 -25
  14. package/dist/flutter.js.map +1 -1
  15. package/dist/go-dependencies.js +389 -0
  16. package/dist/go-dependencies.js.map +1 -0
  17. package/dist/inbox.js +7 -8
  18. package/dist/inbox.js.map +1 -1
  19. package/dist/integrations.js +30 -3
  20. package/dist/integrations.js.map +1 -1
  21. package/dist/isolation.js +190 -0
  22. package/dist/isolation.js.map +1 -0
  23. package/dist/macos.js +57 -0
  24. package/dist/macos.js.map +1 -0
  25. package/dist/mygo-build.js +55 -0
  26. package/dist/mygo-build.js.map +1 -0
  27. package/dist/policy.js +22 -0
  28. package/dist/policy.js.map +1 -0
  29. package/dist/preflight.js +1 -1
  30. package/dist/process.js +3 -3
  31. package/dist/project-profiles.js +163 -0
  32. package/dist/project-profiles.js.map +1 -0
  33. package/dist/proof.js +33 -0
  34. package/dist/proof.js.map +1 -0
  35. package/dist/remote.js +1 -1
  36. package/dist/runner.js +269 -287
  37. package/dist/runner.js.map +1 -1
  38. package/dist/scheduler.js +0 -3
  39. package/dist/scheduler.js.map +1 -1
  40. package/dist/schemas.js +156 -44
  41. package/dist/schemas.js.map +1 -1
  42. package/dist/store.js +166 -44
  43. package/dist/store.js.map +1 -1
  44. package/dist/vendor/obfuscator/dist/cli.js +72 -0
  45. package/dist/vendor/obfuscator/dist/cli.js.map +1 -0
  46. package/dist/vendor/obfuscator/dist/code.js +585 -0
  47. package/dist/vendor/obfuscator/dist/code.js.map +1 -0
  48. package/dist/vendor/obfuscator/dist/common.js +262 -0
  49. package/dist/vendor/obfuscator/dist/common.js.map +1 -0
  50. package/dist/vendor/obfuscator/dist/config.js +123 -0
  51. package/dist/vendor/obfuscator/dist/config.js.map +1 -0
  52. package/dist/vendor/obfuscator/dist/content.js +89 -0
  53. package/dist/vendor/obfuscator/dist/content.js.map +1 -0
  54. package/dist/vendor/obfuscator/dist/obfuscate.js +93 -0
  55. package/dist/vendor/obfuscator/dist/obfuscate.js.map +1 -0
  56. package/dist/vendor/obfuscator/dist/png.js +283 -0
  57. package/dist/vendor/obfuscator/dist/png.js.map +1 -0
  58. package/dist/vendor/obfuscator/dist/project.js +159 -0
  59. package/dist/vendor/obfuscator/dist/project.js.map +1 -0
  60. package/dist/vendor/obfuscator/dist/resources.js +335 -0
  61. package/dist/vendor/obfuscator/dist/resources.js.map +1 -0
  62. package/dist/vendor/obfuscator/package.json +22 -0
  63. package/dist/workspace.js +113 -0
  64. package/dist/workspace.js.map +1 -0
  65. package/evals/control.mjs +73 -0
  66. package/evals/expectations.mjs +37 -0
  67. package/evals/host.mjs +51 -0
  68. package/evals/oracle.mjs +80 -0
  69. package/evals/policy.mjs +268 -0
  70. package/examples/android-java.factory.json +18 -0
  71. package/examples/android-java.json +16 -0
  72. package/examples/existing-project.factory.json +18 -0
  73. package/examples/existing-project.json +17 -0
  74. package/examples/expo.eas.json +17 -0
  75. package/examples/expo.factory.json +18 -0
  76. package/examples/expo.json +16 -0
  77. package/examples/factory.json +0 -2
  78. package/examples/flutter.factory.json +13 -0
  79. package/examples/flutter.json +16 -0
  80. package/examples/luggage.json +5 -3
  81. package/examples/mygo-web.factory.json +18 -0
  82. package/examples/mygo-web.json +16 -0
  83. package/examples/mygo.factory.json +18 -0
  84. package/examples/mygo.json +16 -0
  85. package/examples/react-native.factory.json +18 -0
  86. package/examples/react-native.json +16 -0
  87. package/examples/runtime/android-java/app/build.gradle +7 -0
  88. package/examples/runtime/android-java/app/src/main/AndroidManifest.xml +8 -0
  89. package/examples/runtime/android-java/app/src/main/java/dev/apphx/runtime/MainActivity.java +44 -0
  90. package/examples/runtime/android-java/sdk-build.mjs +29 -0
  91. package/examples/runtime/android-java/settings.gradle +2 -0
  92. package/examples/runtime/android-ui.mjs +70 -0
  93. package/examples/runtime/mygo-native/go.mod +13 -0
  94. package/examples/runtime/mygo-native/go.sum +10 -0
  95. package/examples/runtime/mygo-native/main.go +173 -0
  96. package/examples/runtime/mygo-native/main_test.go +14 -0
  97. package/examples/runtime/mygo-native/mygo.json +1 -0
  98. package/examples/runtime/mygo-web/bun.lock +15 -0
  99. package/examples/runtime/mygo-web/go.mod +9 -0
  100. package/examples/runtime/mygo-web/go.sum +8 -0
  101. package/examples/runtime/mygo-web/index.html +1 -0
  102. package/examples/runtime/mygo-web/main.go +60 -0
  103. package/examples/runtime/mygo-web/mygo.config.ts +10 -0
  104. package/examples/runtime/mygo-web/package.json +1 -0
  105. package/examples/runtime/mygo-web/src/main.ts +23 -0
  106. package/examples/runtime/mygo-web/src/mygo.ts +14 -0
  107. package/examples/swift.factory.json +18 -0
  108. package/examples/swift.json +16 -0
  109. package/examples/uni-app.factory.json +18 -0
  110. package/examples/uni-app.json +16 -0
  111. package/package.json +12 -5
  112. package/scripts/bundle-obfuscator.mjs +19 -0
  113. package/scripts/runtime-smoke.mjs +209 -0
  114. package/skills/apphx/SKILL.md +94 -17
  115. package/skills/apphx/resources/google-play-research-result.json +6 -0
  116. package/skills/apphx/resources/local-research-result.json +6 -0
  117. package/skills/apphx/resources/requirement.json +5 -8
  118. package/skills/apphx/resources/research-result.json +5 -14
  119. package/skills/apphx/resources/review-result.json +1 -1
  120. package/dist/apple.js +0 -900
  121. package/dist/apple.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"schemas.js","names":[],"sources":["../src/schemas.ts"],"sourcesContent":["import { z } from 'zod';\nimport { AppleReceiptSchema } from './apple-types.js';\n\nconst text = z.string().min(1).refine(value => value.trim().length > 0 && !value.includes('\\0'), 'Expected non-empty text without NUL characters');\nconst count = z.number().int().nonnegative().safe();\nconst positive = z.number().positive().finite();\nconst command = z.array(text).min(1);\n\n\nexport const AppleSpecSchema = z.strictObject({\n appId: text.optional(), bundleId: text.optional(), version: text.optional(), buildNumber: text.optional(), locale: text.optional(),\n description: text.optional(), keywords: text.optional(), supportUrl: text.optional(), privacyPolicyUrl: text.optional(), reviewNotes: text.optional(),\n copyright: text.optional(), whatsNew: text.optional(), storefrontCountry: text.optional(), screenshotsDir: text.optional(), manualSetupConfirmed: z.boolean().optional(),\n contact: z.strictObject({ firstName: text.optional(), lastName: text.optional(), email: text.optional(), phone: text.optional() }).optional(),\n});\nexport type AppleSpec = z.infer<typeof AppleSpecSchema>;\nexport const SpecSchema = z.strictObject({\n id: text.regex(/^[a-z][a-z0-9-]{1,62}$/), name: text, purpose: text, audience: text,\n framework: z.literal('flutter'), differentiators: z.array(text).min(1), acceptance: z.array(text).min(1),\n constraints: z.array(text).optional(), apple: AppleSpecSchema.optional(),\n hiddenFeatures: z.literal(false).optional(), reviewBypass: z.literal(false).optional(),\n});\nexport type Spec = z.infer<typeof SpecSchema>;\nexport const RequirementSchema = SpecSchema.extend({ baseJobId: z.uuid().optional() });\n\nexport const ConfigSchema = z.strictObject({\n configPath: text.optional(), stateDir: text, workspaceRoot: text,\n allowLocalExecution: z.boolean().default(false), allowSubmit: z.boolean().default(false), allowRelease: z.boolean().default(false),\n executionEnv: z.array(text.regex(/^[A-Z][A-Z0-9_]*$/).refine(value => !/^(ASC_|APPLE_|FASTLANE_|MATCH_)/.test(value), 'Publisher credentials must not be passed to host or verification tools')).default([]),\n timeoutMs: positive.default(900000), maxAttempts: positive.int().default(3), maxHostTurns: positive.int().default(16), maxJobHours: positive.default(24),\n retryDelayMs: positive.default(30000), pollMs: positive.default(300000),\n inbox: z.strictObject({ directory: text, pollMs: positive.default(2000) }).optional(),\n verification: z.strictObject({ prerequisites: z.array(command).default([]), checks: z.array(command).min(1), runtime: z.array(command).min(1) }),\n toolchain: z.strictObject({ flutterSdk: text }).optional(),\n apple: z.strictObject({ exportOptionsPlist: text.optional() }).optional(),\n packageTarget: z.enum(['simulator', 'ipa']).optional(), executionWarning: text.optional(),\n}).transform(value => ({ ...value, packageTarget: value.packageTarget ?? (value.allowSubmit ? 'ipa' as const : 'simulator' as const) })).superRefine((value, context) => {\n if (value.allowSubmit && value.packageTarget !== 'ipa') context.addIssue({ code: 'custom', path: ['packageTarget'], message: 'Submission requires packageTarget=ipa; simulator builds cannot be uploaded' });\n if (value.allowRelease && !value.allowSubmit) context.addIssue({ code: 'custom', path: ['allowRelease'], message: 'allowRelease requires allowSubmit' });\n});\nexport type Config = z.infer<typeof ConfigSchema>;\nexport const StageSchema = z.enum(['research', 'build', 'verify', 'review', 'package', 'publish', 'monitor']);\nexport type Stage = z.infer<typeof StageSchema>;\nexport const StatusSchema = z.enum(['queued', 'running', 'waiting', 'blocked', 'failed', 'ready', 'built', 'approved', 'released']);\nexport type Status = z.infer<typeof StatusSchema>;\nexport const ToolchainSchema = z.strictObject({ source: text, frameworkRevision: text, engineRevision: text, ready: z.boolean() });\nexport type Toolchain = z.infer<typeof ToolchainSchema>;\nexport const ProcessIdentitySchema = z.strictObject({ token: text });\nexport type ProcessIdentity = z.infer<typeof ProcessIdentitySchema>;\nexport const CommandMetadataSchema = z.strictObject({ deadline: z.number().finite(), identity: ProcessIdentitySchema, termAt: z.number().finite().optional(), killAt: z.number().finite().optional() });\nexport type CommandMetadata = z.infer<typeof CommandMetadataSchema>;\nexport const PrerequisiteSchema = z.strictObject({ argv: z.array(text).optional(), reason: text, path: text.optional() });\nexport type Prerequisite = z.infer<typeof PrerequisiteSchema>;\nexport const ResearchSchema = z.strictObject({\n viable: z.boolean(), policySources: z.array(z.strictObject({ rule: text, url: z.url() })), distinctness: text, risks: z.array(text).optional(), plan: z.array(text).min(1),\n});\nexport type Research = z.infer<typeof ResearchSchema>;\nexport const ReviewSchema = z.strictObject({ approved: z.boolean(), findings: z.array(text), acceptanceEvidence: z.array(z.strictObject({ requirement: text, evidence: text })) });\nexport type Review = z.infer<typeof ReviewSchema>;\nexport const ArtifactSchema = z.strictObject({ kind: z.enum(['ios-simulator', 'ipa']), path: text, digest: text.regex(/^[a-f0-9]{64}$/), createdAt: z.iso.datetime() });\nexport type Artifact = z.infer<typeof ArtifactSchema>;\nexport const ApplePollResultSchema = z.strictObject({ status: z.enum(['waiting', 'approved', 'rejected']), detail: text, distributionReady: z.boolean().optional(), available: z.boolean().optional() });\nexport const InboxEntrySchema = z.strictObject({\n source: text, digest: text.nullable(), status: z.enum(['accepted', 'error']), jobId: text.nullable(),\n error: z.string().nullable(), observedAt: z.number().finite(),\n});\nexport type InboxEntry = z.infer<typeof InboxEntrySchema>;\nexport const HostStageSchema = z.enum(['research', 'build', 'review']);\nexport type HostStage = z.infer<typeof HostStageSchema>;\nexport const HostTurnSchema = z.strictObject({\n id: z.uuid(), stage: HostStageSchema, createdAt: z.number().finite(), contextDigest: text.regex(/^[a-f0-9]{64}$/),\n});\nexport type HostTurn = z.infer<typeof HostTurnSchema>;\nexport const JobSchema = z.object({\n id: text, spec: SpecSchema, requestSpec: SpecSchema.nullable(), requestSpecError: text.nullable().default(null), config: ConfigSchema, stage: StageSchema, status: StatusSchema,\n createdAt: z.number().finite(), updatedAt: z.number().finite(), nextAt: z.number().finite(),\n attempts: z.partialRecord(StageSchema, count), hostTurns: count, turn: HostTurnSchema.nullable().optional(), groups: z.array(z.number().int()),\n commands: z.record(z.string(), CommandMetadataSchema).default({}), receipt: AppleReceiptSchema, logs: z.array(text), workspace: text,\n baseJobId: z.uuid().optional(), baseDigest: text.regex(/^[a-f0-9]{64}$/).optional(), basePrepared: z.boolean().optional(),\n recoveryStage: StageSchema.optional(),\n blockedReason: z.strictObject({ kind: z.literal('prerequisite'), stage: StageSchema, prerequisite: PrerequisiteSchema }).nullable().optional(),\n manualRecovery: z.string().nullable().optional(), error: z.string().nullable().optional(), feedback: z.string().optional(),\n research: ResearchSchema.optional(), policyEvidence: z.strictObject({ url: z.url(), fetchedAt: text, sha256: text }).optional(),\n toolchain: ToolchainSchema.optional(), verifiedDigest: text.nullable().optional(), verifiedDependencyDigest: text.regex(/^[a-f0-9]{64}$/).nullable().optional(), screenshotDigest: text.nullable().optional(), verifiedAt: text.optional(),\n review: ReviewSchema.nullable().optional(), ipaPath: text.nullable().optional(), archiveDigest: text.nullable().optional(), artifact: ArtifactSchema.nullable().optional(),\n appleReview: ApplePollResultSchema.optional(), externalChecks: z.unknown().optional(),\n}).passthrough();\nexport type Job = z.infer<typeof JobSchema>;\n\nexport type FactoryError = Error & { code?: string; syscall?: string; prerequisite?: Prerequisite; httpStatus?: number | null; outcome?: string; transient?: boolean };\nexport function toError(value: unknown): FactoryError {\n return value instanceof Error ? value : new Error(String(value));\n}\n"],"mappings":";;;AAGA,MAAM,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAO,UAAS,MAAM,KAAK,CAAC,CAAC,SAAS,KAAK,CAAC,MAAM,SAAS,IAAI,GAAG,gDAAgD;AACjJ,MAAM,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,CAAC,KAAK;AAClD,MAAM,WAAW,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO;AAC9C,MAAM,UAAU,EAAE,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC;AAGnC,MAAa,kBAAkB,EAAE,aAAa;CAC5C,OAAO,KAAK,SAAS;CAAG,UAAU,KAAK,SAAS;CAAG,SAAS,KAAK,SAAS;CAAG,aAAa,KAAK,SAAS;CAAG,QAAQ,KAAK,SAAS;CACjI,aAAa,KAAK,SAAS;CAAG,UAAU,KAAK,SAAS;CAAG,YAAY,KAAK,SAAS;CAAG,kBAAkB,KAAK,SAAS;CAAG,aAAa,KAAK,SAAS;CACpJ,WAAW,KAAK,SAAS;CAAG,UAAU,KAAK,SAAS;CAAG,mBAAmB,KAAK,SAAS;CAAG,gBAAgB,KAAK,SAAS;CAAG,sBAAsB,EAAE,QAAQ,CAAC,CAAC,SAAS;CACvK,SAAS,EAAE,aAAa;EAAE,WAAW,KAAK,SAAS;EAAG,UAAU,KAAK,SAAS;EAAG,OAAO,KAAK,SAAS;EAAG,OAAO,KAAK,SAAS;CAAE,CAAC,CAAC,CAAC,SAAS;AAC9I,CAAC;AAED,MAAa,aAAa,EAAE,aAAa;CACvC,IAAI,KAAK,MAAM,wBAAwB;CAAG,MAAM;CAAM,SAAS;CAAM,UAAU;CAC/E,WAAW,EAAE,QAAQ,SAAS;CAAG,iBAAiB,EAAE,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC;CAAG,YAAY,EAAE,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC;CACvG,aAAa,EAAE,MAAM,IAAI,CAAC,CAAC,SAAS;CAAG,OAAO,gBAAgB,SAAS;CACvE,gBAAgB,EAAE,QAAQ,KAAK,CAAC,CAAC,SAAS;CAAG,cAAc,EAAE,QAAQ,KAAK,CAAC,CAAC,SAAS;AACvF,CAAC;AAED,MAAa,oBAAoB,WAAW,OAAO,EAAE,WAAW,EAAE,KAAK,CAAC,CAAC,SAAS,EAAE,CAAC;AAErF,MAAa,eAAe,EAAE,aAAa;CACzC,YAAY,KAAK,SAAS;CAAG,UAAU;CAAM,eAAe;CAC5D,qBAAqB,EAAE,QAAQ,CAAC,CAAC,QAAQ,KAAK;CAAG,aAAa,EAAE,QAAQ,CAAC,CAAC,QAAQ,KAAK;CAAG,cAAc,EAAE,QAAQ,CAAC,CAAC,QAAQ,KAAK;CACjI,cAAc,EAAE,MAAM,KAAK,MAAM,mBAAmB,CAAC,CAAC,QAAO,UAAS,CAAC,kCAAkC,KAAK,KAAK,GAAG,wEAAwE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;CAC3M,WAAW,SAAS,QAAQ,GAAM;CAAG,aAAa,SAAS,IAAI,CAAC,CAAC,QAAQ,CAAC;CAAG,cAAc,SAAS,IAAI,CAAC,CAAC,QAAQ,EAAE;CAAG,aAAa,SAAS,QAAQ,EAAE;CACvJ,cAAc,SAAS,QAAQ,GAAK;CAAG,QAAQ,SAAS,QAAQ,GAAM;CACtE,OAAO,EAAE,aAAa;EAAE,WAAW;EAAM,QAAQ,SAAS,QAAQ,GAAI;CAAE,CAAC,CAAC,CAAC,SAAS;CACpF,cAAc,EAAE,aAAa;EAAE,eAAe,EAAE,MAAM,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC;EAAG,QAAQ,EAAE,MAAM,OAAO,CAAC,CAAC,IAAI,CAAC;EAAG,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,IAAI,CAAC;CAAE,CAAC;CAC/I,WAAW,EAAE,aAAa,EAAE,YAAY,KAAK,CAAC,CAAC,CAAC,SAAS;CACzD,OAAO,EAAE,aAAa,EAAE,oBAAoB,KAAK,SAAS,EAAE,CAAC,CAAC,CAAC,SAAS;CACxE,eAAe,EAAE,KAAK,CAAC,aAAa,KAAK,CAAC,CAAC,CAAC,SAAS;CAAG,kBAAkB,KAAK,SAAS;AAC1F,CAAC,CAAC,CAAC,WAAU,WAAU;CAAE,GAAG;CAAO,eAAe,MAAM,kBAAkB,MAAM,cAAc,QAAiB;AAAsB,EAAE,CAAC,CAAC,aAAa,OAAO,YAAY;CACvK,IAAI,MAAM,eAAe,MAAM,kBAAkB,OAAO,QAAQ,SAAS;EAAE,MAAM;EAAU,MAAM,CAAC,eAAe;EAAG,SAAS;CAA6E,CAAC;CAC3M,IAAI,MAAM,gBAAgB,CAAC,MAAM,aAAa,QAAQ,SAAS;EAAE,MAAM;EAAU,MAAM,CAAC,cAAc;EAAG,SAAS;CAAoC,CAAC;AACzJ,CAAC;AAED,MAAa,cAAc,EAAE,KAAK;CAAC;CAAY;CAAS;CAAU;CAAU;CAAW;CAAW;AAAS,CAAC;AAE5G,MAAa,eAAe,EAAE,KAAK;CAAC;CAAU;CAAW;CAAW;CAAW;CAAU;CAAS;CAAS;CAAY;AAAU,CAAC;AAElI,MAAa,kBAAkB,EAAE,aAAa;CAAE,QAAQ;CAAM,mBAAmB;CAAM,gBAAgB;CAAM,OAAO,EAAE,QAAQ;AAAE,CAAC;AAEjI,MAAa,wBAAwB,EAAE,aAAa,EAAE,OAAO,KAAK,CAAC;AAEnE,MAAa,wBAAwB,EAAE,aAAa;CAAE,UAAU,EAAE,OAAO,CAAC,CAAC,OAAO;CAAG,UAAU;CAAuB,QAAQ,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,SAAS;CAAG,QAAQ,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,SAAS;AAAE,CAAC;AAEtM,MAAa,qBAAqB,EAAE,aAAa;CAAE,MAAM,EAAE,MAAM,IAAI,CAAC,CAAC,SAAS;CAAG,QAAQ;CAAM,MAAM,KAAK,SAAS;AAAE,CAAC;AAExH,MAAa,iBAAiB,EAAE,aAAa;CAC3C,QAAQ,EAAE,QAAQ;CAAG,eAAe,EAAE,MAAM,EAAE,aAAa;EAAE,MAAM;EAAM,KAAK,EAAE,IAAI;CAAE,CAAC,CAAC;CAAG,cAAc;CAAM,OAAO,EAAE,MAAM,IAAI,CAAC,CAAC,SAAS;CAAG,MAAM,EAAE,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC;AAC3K,CAAC;AAED,MAAa,eAAe,EAAE,aAAa;CAAE,UAAU,EAAE,QAAQ;CAAG,UAAU,EAAE,MAAM,IAAI;CAAG,oBAAoB,EAAE,MAAM,EAAE,aAAa;EAAE,aAAa;EAAM,UAAU;CAAK,CAAC,CAAC;AAAE,CAAC;AAEjL,MAAa,iBAAiB,EAAE,aAAa;CAAE,MAAM,EAAE,KAAK,CAAC,iBAAiB,KAAK,CAAC;CAAG,MAAM;CAAM,QAAQ,KAAK,MAAM,gBAAgB;CAAG,WAAW,EAAE,IAAI,SAAS;AAAE,CAAC;AAEtK,MAAa,wBAAwB,EAAE,aAAa;CAAE,QAAQ,EAAE,KAAK;EAAC;EAAW;EAAY;CAAU,CAAC;CAAG,QAAQ;CAAM,mBAAmB,EAAE,QAAQ,CAAC,CAAC,SAAS;CAAG,WAAW,EAAE,QAAQ,CAAC,CAAC,SAAS;AAAE,CAAC;AACvM,MAAa,mBAAmB,EAAE,aAAa;CAC7C,QAAQ;CAAM,QAAQ,KAAK,SAAS;CAAG,QAAQ,EAAE,KAAK,CAAC,YAAY,OAAO,CAAC;CAAG,OAAO,KAAK,SAAS;CACnG,OAAO,EAAE,OAAO,CAAC,CAAC,SAAS;CAAG,YAAY,EAAE,OAAO,CAAC,CAAC,OAAO;AAC9D,CAAC;AAED,MAAa,kBAAkB,EAAE,KAAK;CAAC;CAAY;CAAS;AAAQ,CAAC;AAErE,MAAa,iBAAiB,EAAE,aAAa;CAC3C,IAAI,EAAE,KAAK;CAAG,OAAO;CAAiB,WAAW,EAAE,OAAO,CAAC,CAAC,OAAO;CAAG,eAAe,KAAK,MAAM,gBAAgB;AAClH,CAAC;AAED,MAAa,YAAY,EAAE,OAAO;CAChC,IAAI;CAAM,MAAM;CAAY,aAAa,WAAW,SAAS;CAAG,kBAAkB,KAAK,SAAS,CAAC,CAAC,QAAQ,IAAI;CAAG,QAAQ;CAAc,OAAO;CAAa,QAAQ;CACnK,WAAW,EAAE,OAAO,CAAC,CAAC,OAAO;CAAG,WAAW,EAAE,OAAO,CAAC,CAAC,OAAO;CAAG,QAAQ,EAAE,OAAO,CAAC,CAAC,OAAO;CAC1F,UAAU,EAAE,cAAc,aAAa,KAAK;CAAG,WAAW;CAAO,MAAM,eAAe,SAAS,CAAC,CAAC,SAAS;CAAG,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;CAC7I,UAAU,EAAE,OAAO,EAAE,OAAO,GAAG,qBAAqB,CAAC,CAAC,QAAQ,CAAC,CAAC;CAAG,SAAS;CAAoB,MAAM,EAAE,MAAM,IAAI;CAAG,WAAW;CAChI,WAAW,EAAE,KAAK,CAAC,CAAC,SAAS;CAAG,YAAY,KAAK,MAAM,gBAAgB,CAAC,CAAC,SAAS;CAAG,cAAc,EAAE,QAAQ,CAAC,CAAC,SAAS;CACxH,eAAe,YAAY,SAAS;CACpC,eAAe,EAAE,aAAa;EAAE,MAAM,EAAE,QAAQ,cAAc;EAAG,OAAO;EAAa,cAAc;CAAmB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS;CAC7I,gBAAgB,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS;CAAG,OAAO,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS;CAAG,UAAU,EAAE,OAAO,CAAC,CAAC,SAAS;CACzH,UAAU,eAAe,SAAS;CAAG,gBAAgB,EAAE,aAAa;EAAE,KAAK,EAAE,IAAI;EAAG,WAAW;EAAM,QAAQ;CAAK,CAAC,CAAC,CAAC,SAAS;CAC9H,WAAW,gBAAgB,SAAS;CAAG,gBAAgB,KAAK,SAAS,CAAC,CAAC,SAAS;CAAG,0BAA0B,KAAK,MAAM,gBAAgB,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS;CAAG,kBAAkB,KAAK,SAAS,CAAC,CAAC,SAAS;CAAG,YAAY,KAAK,SAAS;CACzO,QAAQ,aAAa,SAAS,CAAC,CAAC,SAAS;CAAG,SAAS,KAAK,SAAS,CAAC,CAAC,SAAS;CAAG,eAAe,KAAK,SAAS,CAAC,CAAC,SAAS;CAAG,UAAU,eAAe,SAAS,CAAC,CAAC,SAAS;CACzK,aAAa,sBAAsB,SAAS;CAAG,gBAAgB,EAAE,QAAQ,CAAC,CAAC,SAAS;AACtF,CAAC,CAAC,CAAC,YAAY;AAIf,SAAgB,QAAQ,OAA8B;CACpD,OAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AACjE"}
1
+ {"version":3,"file":"schemas.js","names":[],"sources":["../src/schemas.ts"],"sourcesContent":["import { z } from 'zod';\nimport { dirname, isAbsolute, resolve, win32 } from 'node:path';\nimport { lstatSync } from 'node:fs';\nimport { AppleReceiptSchema } from './apple-types.js';\nimport { validateMyGoPackaging } from './mygo-build.js';\n\nconst text = z.string().min(1).refine(value => value.trim().length > 0 && !value.includes('\\0'), 'Expected non-empty text without NUL characters');\nconst count = z.number().int().nonnegative().safe();\nconst positive = z.number().positive().finite();\nconst command = z.array(text).min(1);\n\nfunction relativeProjectPath(value: string, allowRoot = false): boolean {\n if (allowRoot && value === '.') return true;\n return !isAbsolute(value) && !win32.isAbsolute(value) && !/[\\\\:*?\\[\\]]/.test(value)\n && value.split('/').every(part => part !== '' && part !== '.' && part !== '..');\n}\n\nexport const FrameworkSchema = z.enum(['flutter', 'android-java', 'swift', 'react-native', 'expo', 'uni-app', 'mygo']);\nexport type Framework = z.infer<typeof FrameworkSchema>;\nexport const PlatformSchema = z.enum(['ios', 'android', 'macos']);\nexport type Platform = z.infer<typeof PlatformSchema>;\nexport const ChannelSchema = z.enum(['local', 'app-store', 'google-play']);\nexport type Channel = z.infer<typeof ChannelSchema>;\nexport const PackageTargetSchema = z.enum(['simulator', 'ipa', 'apk', 'aab', 'app', 'dmg']);\nexport type PackageTarget = z.infer<typeof PackageTargetSchema>;\nexport const AppleSpecSchema = z.strictObject({\n appId: text.optional(), bundleId: text.optional(), buildNumber: text.optional(), locale: text.optional(),\n description: text.optional(), keywords: text.optional(), supportUrl: text.optional(), privacyPolicyUrl: text.optional(), reviewNotes: text.optional(),\n copyright: text.optional(), whatsNew: text.optional(), storefrontCountry: text.optional(), manualSetupConfirmed: z.boolean().optional(),\n contact: z.strictObject({ firstName: text.optional(), lastName: text.optional(), email: text.optional(), phone: text.optional() }).optional(),\n});\nexport type AppleSpec = z.infer<typeof AppleSpecSchema>;\nconst SpecObject = z.strictObject({\n id: text.regex(/^[a-z][a-z0-9-]{1,62}$/), name: text, purpose: text, audience: text,\n framework: FrameworkSchema, platform: PlatformSchema, channel: ChannelSchema.default('local'), version: text.default('1.0.0'),\n project: z.strictObject({ sourceDir: text.optional(), directory: text.refine(value => relativeProjectPath(value, true), 'project.directory must be a relative in-root directory without traversal').default('.') }).optional(),\n screenshotsDir: text.optional(), differentiators: z.array(text).min(1), acceptance: z.array(text).min(1),\n constraints: z.array(text).optional(), apple: AppleSpecSchema.optional(),\n hiddenFeatures: z.literal(false).optional(), reviewBypass: z.literal(false).optional(),\n});\nfunction refineSpec(value: z.infer<typeof SpecObject>, context: z.RefinementCtx): void {\n if (value.framework === 'android-java' && value.platform !== 'android') context.addIssue({ code: 'custom', path: ['platform'], message: 'android-java requires platform=android' });\n if (value.framework === 'swift' && value.platform !== 'ios') context.addIssue({ code: 'custom', path: ['platform'], message: 'swift requires platform=ios' });\n if (value.framework === 'mygo' && value.platform !== 'macos') context.addIssue({ code: 'custom', path: ['platform'], message: 'mygo requires platform=macos' });\n if (value.platform === 'macos' && value.framework !== 'mygo') context.addIssue({ code: 'custom', path: ['framework'], message: 'macos currently requires framework=mygo' });\n if (value.platform === 'macos' && value.channel !== 'local') context.addIssue({ code: 'custom', path: ['channel'], message: 'macos currently supports local delivery only' });\n if (value.channel === 'app-store' && value.platform !== 'ios') context.addIssue({ code: 'custom', path: ['channel'], message: 'app-store requires platform=ios' });\n if (value.channel === 'google-play' && value.platform !== 'android') context.addIssue({ code: 'custom', path: ['channel'], message: 'google-play requires platform=android' });\n}\nexport const SpecSchema = SpecObject.superRefine(refineSpec);\nexport type Spec = z.infer<typeof SpecSchema>;\nexport const RequirementSchema = SpecObject.extend({ baseJobId: z.uuid().optional() }).superRefine((value, context) => {\n refineSpec(value, context);\n if (value.baseJobId && value.project?.sourceDir) context.addIssue({ code: 'custom', path: ['project', 'sourceDir'], message: 'project.sourceDir and baseJobId are mutually exclusive' });\n});\n\nexport const ObfuscationConfigSchema = z.strictObject({\n command: z.array(command.element).min(1).default(['obfuscator']),\n configPath: text,\n seed: text.optional(),\n});\nexport type ObfuscationConfig = z.infer<typeof ObfuscationConfigSchema>;\n\nexport const ConfigSchema = z.strictObject({\n configPath: text.optional(), stateDir: text, workspaceRoot: text,\n allowLocalExecution: z.boolean().default(false), allowCloudBuild: z.boolean().default(false),\n executionEnv: z.array(text.regex(/^[A-Z][A-Z0-9_]*$/).refine(value => !/^(ASC_|APPLE_|FASTLANE_|MATCH_)/.test(value), 'Publisher credentials must not be passed to host or verification tools')).default([]),\n isolation: z.strictObject({ mode: z.literal('macos-sandbox'), readOnlyPaths: z.array(text).default([]), network: z.enum(['deny', 'allow']).default('deny') }).optional(),\n timeoutMs: positive.default(900000), maxAttempts: positive.int().default(3), maxHostTurns: positive.int().default(16), maxJobHours: positive.default(24),\n retryDelayMs: positive.default(30000), pollMs: positive.default(300000),\n inbox: z.strictObject({ directory: text, pollMs: positive.default(2000) }).optional(),\n verification: z.strictObject({ prerequisites: z.array(command).default([]), checks: z.array(command).min(1), runtime: z.array(command).min(1) }),\n toolchain: z.strictObject({ flutterSdk: text }).optional(),\n apple: z.strictObject({ exportOptionsPlist: text.optional() }).optional(),\n packageTarget: PackageTargetSchema, executionWarning: text.optional(),\n obfuscation: ObfuscationConfigSchema.optional(),\n packaging: z.strictObject({ commands: z.array(command).min(1), artifactPath: text.refine(value => relativeProjectPath(value), 'artifactPath must be an exact project-relative file or directory without traversal or wildcards'), provider: z.enum(['local', 'cloud']).default('local') }).optional(),\n}).superRefine((value, context) => {\n if (value.packaging?.provider === 'cloud' && !value.allowCloudBuild) context.addIssue({ code: 'custom', path: ['allowCloudBuild'], message: 'Cloud packaging requires explicit allowCloudBuild=true' });\n});\nexport type Config = z.infer<typeof ConfigSchema>;\nexport function validateJobConfig(spec: Spec, config: Config): void {\n const targets: Record<Platform, readonly Config['packageTarget'][]> = { ios: ['simulator', 'ipa'], android: ['apk', 'aab'], macos: ['app', 'dmg'] };\n if (!targets[spec.platform].includes(config.packageTarget)) throw new Error(`packageTarget=${config.packageTarget} does not match platform=${spec.platform}`);\n if (spec.channel === 'google-play' && config.packageTarget !== 'aab') throw new Error('Google Play requires packageTarget=aab');\n if (config.toolchain && spec.framework !== 'flutter') throw new Error('toolchain.flutterSdk is only supported for framework=flutter');\n if (spec.framework !== 'flutter' && !config.packaging) throw new Error(`${spec.framework} requires owner-configured packaging.commands and packaging.artifactPath`);\n if (config.packaging?.provider === 'cloud' && !config.allowCloudBuild) throw new Error('Cloud packaging requires explicit allowCloudBuild=true');\n if (spec.platform === 'macos' && (config.allowCloudBuild || config.packaging?.provider === 'cloud')) throw new Error('macos currently supports local build-only delivery; notarization and uploads are not part of this tool');\n if (spec.framework === 'mygo') validateMyGoPackaging(config, spec.screenshotsDir);\n}\nexport const StageSchema = z.enum(['research', 'build', 'obfuscate', 'verify', 'review', 'package']);\nexport type Stage = z.infer<typeof StageSchema>;\nexport const StatusSchema = z.enum(['queued', 'running', 'waiting', 'blocked', 'failed', 'ready', 'built']);\nexport type Status = z.infer<typeof StatusSchema>;\nexport const ToolchainSchema = z.strictObject({ source: text, frameworkRevision: text, engineRevision: text, ready: z.boolean() });\nexport type Toolchain = z.infer<typeof ToolchainSchema>;\nexport const ProcessIdentitySchema = z.strictObject({ token: text });\nexport type ProcessIdentity = z.infer<typeof ProcessIdentitySchema>;\nexport const CommandMetadataSchema = z.strictObject({ deadline: z.number().finite(), identity: ProcessIdentitySchema, termAt: z.number().finite().optional(), killAt: z.number().finite().optional() });\nexport type CommandMetadata = z.infer<typeof CommandMetadataSchema>;\nexport const PrerequisiteSchema = z.strictObject({ argv: z.array(text).optional(), reason: text, path: text.optional() });\nexport type Prerequisite = z.infer<typeof PrerequisiteSchema>;\nexport const ResearchSchema = z.strictObject({\n viable: z.boolean(), distinctness: text, risks: z.array(text).optional(), plan: z.array(text).min(1),\n});\nexport type Research = z.infer<typeof ResearchSchema>;\nexport const ReviewSchema = z.strictObject({ approved: z.boolean(), findings: z.array(text), acceptanceEvidence: z.array(z.strictObject({ requirement: text, evidence: text })) });\nexport type Review = z.infer<typeof ReviewSchema>;\nexport const ArtifactSchema = z.strictObject({ kind: z.enum(['ios-simulator', 'ipa', 'apk', 'aab', 'macos-app', 'dmg']), path: text, digest: text.regex(/^[a-f0-9]{64}$/), createdAt: z.iso.datetime() });\nexport type Artifact = z.infer<typeof ArtifactSchema>;\nexport const InboxEntrySchema = z.strictObject({\n source: text, digest: text.nullable(), status: z.enum(['accepted', 'error']), jobId: text.nullable(),\n error: z.string().nullable(), observedAt: z.number().finite(),\n});\nexport type InboxEntry = z.infer<typeof InboxEntrySchema>;\nexport const HostStageSchema = z.enum(['research', 'build', 'review']);\nexport type HostStage = z.infer<typeof HostStageSchema>;\nexport const HostTurnSchema = z.strictObject({\n id: z.uuid(), stage: HostStageSchema, createdAt: z.number().finite(), contextDigest: text.regex(/^[a-f0-9]{64}$/),\n});\nexport type HostTurn = z.infer<typeof HostTurnSchema>;\nexport const JobSchema = z.object({\n id: text, spec: SpecSchema, requestSpec: SpecSchema.nullable(), requestSpecError: text.nullable().default(null), config: ConfigSchema, stage: StageSchema, status: StatusSchema,\n createdAt: z.number().finite(), updatedAt: z.number().finite(), nextAt: z.number().finite(),\n attempts: z.partialRecord(StageSchema, count), hostTurns: count, turn: HostTurnSchema.nullable().optional(), groups: z.array(z.number().int()),\n commands: z.record(z.string(), CommandMetadataSchema).default({}), receipt: AppleReceiptSchema, logs: z.array(text), workspace: text,\n baseJobId: z.uuid().optional(), baseDigest: text.regex(/^[a-f0-9]{64}$/).optional(), basePrepared: z.boolean().optional(),\n automaticBase: z.literal(true).optional(),\n sourceDirectory: text.refine(isAbsolute, 'sourceDirectory must be absolute').optional(), importDigest: text.regex(/^[a-f0-9]{64}$/).optional(), importPrepared: z.boolean().optional(), cloudBuildPending: z.boolean().optional(),\n recoveryStage: StageSchema.optional(),\n blockedReason: z.strictObject({ kind: z.literal('prerequisite'), stage: StageSchema, prerequisite: PrerequisiteSchema }).nullable().optional(),\n manualRecovery: z.string().nullable().optional(), error: z.string().nullable().optional(), feedback: z.string().optional(),\n research: ResearchSchema.optional(), policyEvidence: z.strictObject({ url: z.url(), fetchedAt: text, sha256: text }).optional(),\n toolchain: ToolchainSchema.optional(), verifiedDigest: text.nullable().optional(), verifiedDependencyDigest: text.regex(/^[a-f0-9]{64}$/).nullable().optional(), screenshotDigest: text.nullable().optional(), verifiedAt: text.optional(),\n obfuscationBase: z.strictObject({ digest: text.regex(/^[a-f0-9]{64}$/), path: text }).optional(),\n obfuscationDigest: text.regex(/^[a-f0-9]{64}$/).optional(),\n obfuscationReport: z.strictObject({ changedFiles: count, transforms: z.record(text, count), skipped: z.strictObject({ generated: count, unsupported: count }) }).optional(),\n review: ReviewSchema.nullable().optional(), ipaPath: text.nullable().optional(), archiveDigest: text.nullable().optional(), artifact: ArtifactSchema.nullable().optional(),\n externalChecks: z.unknown().optional(),\n}).passthrough();\nexport type Job = z.infer<typeof JobSchema>;\n\nexport type FactoryError = Error & { code?: string; syscall?: string; prerequisite?: Prerequisite; httpStatus?: number | null; outcome?: string; transient?: boolean };\nexport function toError(value: unknown): FactoryError {\n return value instanceof Error ? value : new Error(String(value));\n}\n"],"mappings":";;;;;;AAMA,MAAM,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAO,UAAS,MAAM,KAAK,CAAC,CAAC,SAAS,KAAK,CAAC,MAAM,SAAS,IAAI,GAAG,gDAAgD;AACjJ,MAAM,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,CAAC,KAAK;AAClD,MAAM,WAAW,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO;AAC9C,MAAM,UAAU,EAAE,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC;AAEnC,SAAS,oBAAoB,OAAe,YAAY,OAAgB;CACtE,IAAI,aAAa,UAAU,KAAK,OAAO;CACvC,OAAO,CAAC,WAAW,KAAK,KAAK,CAAC,MAAM,WAAW,KAAK,KAAK,CAAC,cAAc,KAAK,KAAK,KAC7E,MAAM,MAAM,GAAG,CAAC,CAAC,OAAM,SAAQ,SAAS,MAAM,SAAS,OAAO,SAAS,IAAI;AAClF;AAEA,MAAa,kBAAkB,EAAE,KAAK;CAAC;CAAW;CAAgB;CAAS;CAAgB;CAAQ;CAAW;AAAM,CAAC;AAErH,MAAa,iBAAiB,EAAE,KAAK;CAAC;CAAO;CAAW;AAAO,CAAC;AAEhE,MAAa,gBAAgB,EAAE,KAAK;CAAC;CAAS;CAAa;AAAa,CAAC;AAEzE,MAAa,sBAAsB,EAAE,KAAK;CAAC;CAAa;CAAO;CAAO;CAAO;CAAO;AAAK,CAAC;AAE1F,MAAa,kBAAkB,EAAE,aAAa;CAC5C,OAAO,KAAK,SAAS;CAAG,UAAU,KAAK,SAAS;CAAG,aAAa,KAAK,SAAS;CAAG,QAAQ,KAAK,SAAS;CACvG,aAAa,KAAK,SAAS;CAAG,UAAU,KAAK,SAAS;CAAG,YAAY,KAAK,SAAS;CAAG,kBAAkB,KAAK,SAAS;CAAG,aAAa,KAAK,SAAS;CACpJ,WAAW,KAAK,SAAS;CAAG,UAAU,KAAK,SAAS;CAAG,mBAAmB,KAAK,SAAS;CAAG,sBAAsB,EAAE,QAAQ,CAAC,CAAC,SAAS;CACtI,SAAS,EAAE,aAAa;EAAE,WAAW,KAAK,SAAS;EAAG,UAAU,KAAK,SAAS;EAAG,OAAO,KAAK,SAAS;EAAG,OAAO,KAAK,SAAS;CAAE,CAAC,CAAC,CAAC,SAAS;AAC9I,CAAC;AAED,MAAM,aAAa,EAAE,aAAa;CAChC,IAAI,KAAK,MAAM,wBAAwB;CAAG,MAAM;CAAM,SAAS;CAAM,UAAU;CAC/E,WAAW;CAAiB,UAAU;CAAgB,SAAS,cAAc,QAAQ,OAAO;CAAG,SAAS,KAAK,QAAQ,OAAO;CAC5H,SAAS,EAAE,aAAa;EAAE,WAAW,KAAK,SAAS;EAAG,WAAW,KAAK,QAAO,UAAS,oBAAoB,OAAO,IAAI,GAAG,0EAA0E,CAAC,CAAC,QAAQ,GAAG;CAAE,CAAC,CAAC,CAAC,SAAS;CAC7N,gBAAgB,KAAK,SAAS;CAAG,iBAAiB,EAAE,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC;CAAG,YAAY,EAAE,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC;CACvG,aAAa,EAAE,MAAM,IAAI,CAAC,CAAC,SAAS;CAAG,OAAO,gBAAgB,SAAS;CACvE,gBAAgB,EAAE,QAAQ,KAAK,CAAC,CAAC,SAAS;CAAG,cAAc,EAAE,QAAQ,KAAK,CAAC,CAAC,SAAS;AACvF,CAAC;AACD,SAAS,WAAW,OAAmC,SAAgC;CACrF,IAAI,MAAM,cAAc,kBAAkB,MAAM,aAAa,WAAW,QAAQ,SAAS;EAAE,MAAM;EAAU,MAAM,CAAC,UAAU;EAAG,SAAS;CAAyC,CAAC;CAClL,IAAI,MAAM,cAAc,WAAW,MAAM,aAAa,OAAO,QAAQ,SAAS;EAAE,MAAM;EAAU,MAAM,CAAC,UAAU;EAAG,SAAS;CAA8B,CAAC;CAC5J,IAAI,MAAM,cAAc,UAAU,MAAM,aAAa,SAAS,QAAQ,SAAS;EAAE,MAAM;EAAU,MAAM,CAAC,UAAU;EAAG,SAAS;CAA+B,CAAC;CAC9J,IAAI,MAAM,aAAa,WAAW,MAAM,cAAc,QAAQ,QAAQ,SAAS;EAAE,MAAM;EAAU,MAAM,CAAC,WAAW;EAAG,SAAS;CAA0C,CAAC;CAC1K,IAAI,MAAM,aAAa,WAAW,MAAM,YAAY,SAAS,QAAQ,SAAS;EAAE,MAAM;EAAU,MAAM,CAAC,SAAS;EAAG,SAAS;CAA+C,CAAC;CAC5K,IAAI,MAAM,YAAY,eAAe,MAAM,aAAa,OAAO,QAAQ,SAAS;EAAE,MAAM;EAAU,MAAM,CAAC,SAAS;EAAG,SAAS;CAAkC,CAAC;CACjK,IAAI,MAAM,YAAY,iBAAiB,MAAM,aAAa,WAAW,QAAQ,SAAS;EAAE,MAAM;EAAU,MAAM,CAAC,SAAS;EAAG,SAAS;CAAwC,CAAC;AAC/K;AACA,MAAa,aAAa,WAAW,YAAY,UAAU;AAE3D,MAAa,oBAAoB,WAAW,OAAO,EAAE,WAAW,EAAE,KAAK,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,aAAa,OAAO,YAAY;CACrH,WAAW,OAAO,OAAO;CACzB,IAAI,MAAM,aAAa,MAAM,SAAS,WAAW,QAAQ,SAAS;EAAE,MAAM;EAAU,MAAM,CAAC,WAAW,WAAW;EAAG,SAAS;CAAyD,CAAC;AACzL,CAAC;AAED,MAAa,0BAA0B,EAAE,aAAa;CACpD,SAAS,EAAE,MAAM,QAAQ,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC;CAC/D,YAAY;CACZ,MAAM,KAAK,SAAS;AACtB,CAAC;AAGD,MAAa,eAAe,EAAE,aAAa;CACzC,YAAY,KAAK,SAAS;CAAG,UAAU;CAAM,eAAe;CAC5D,qBAAqB,EAAE,QAAQ,CAAC,CAAC,QAAQ,KAAK;CAAG,iBAAiB,EAAE,QAAQ,CAAC,CAAC,QAAQ,KAAK;CAC3F,cAAc,EAAE,MAAM,KAAK,MAAM,mBAAmB,CAAC,CAAC,QAAO,UAAS,CAAC,kCAAkC,KAAK,KAAK,GAAG,wEAAwE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;CAC3M,WAAW,EAAE,aAAa;EAAE,MAAM,EAAE,QAAQ,eAAe;EAAG,eAAe,EAAE,MAAM,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC;EAAG,SAAS,EAAE,KAAK,CAAC,QAAQ,OAAO,CAAC,CAAC,CAAC,QAAQ,MAAM;CAAE,CAAC,CAAC,CAAC,SAAS;CACvK,WAAW,SAAS,QAAQ,GAAM;CAAG,aAAa,SAAS,IAAI,CAAC,CAAC,QAAQ,CAAC;CAAG,cAAc,SAAS,IAAI,CAAC,CAAC,QAAQ,EAAE;CAAG,aAAa,SAAS,QAAQ,EAAE;CACvJ,cAAc,SAAS,QAAQ,GAAK;CAAG,QAAQ,SAAS,QAAQ,GAAM;CACtE,OAAO,EAAE,aAAa;EAAE,WAAW;EAAM,QAAQ,SAAS,QAAQ,GAAI;CAAE,CAAC,CAAC,CAAC,SAAS;CACpF,cAAc,EAAE,aAAa;EAAE,eAAe,EAAE,MAAM,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC;EAAG,QAAQ,EAAE,MAAM,OAAO,CAAC,CAAC,IAAI,CAAC;EAAG,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,IAAI,CAAC;CAAE,CAAC;CAC/I,WAAW,EAAE,aAAa,EAAE,YAAY,KAAK,CAAC,CAAC,CAAC,SAAS;CACzD,OAAO,EAAE,aAAa,EAAE,oBAAoB,KAAK,SAAS,EAAE,CAAC,CAAC,CAAC,SAAS;CACxE,eAAe;CAAqB,kBAAkB,KAAK,SAAS;CACpE,aAAa,wBAAwB,SAAS;CAC9C,WAAW,EAAE,aAAa;EAAE,UAAU,EAAE,MAAM,OAAO,CAAC,CAAC,IAAI,CAAC;EAAG,cAAc,KAAK,QAAO,UAAS,oBAAoB,KAAK,GAAG,iGAAiG;EAAG,UAAU,EAAE,KAAK,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,QAAQ,OAAO;CAAE,CAAC,CAAC,CAAC,SAAS;AACtS,CAAC,CAAC,CAAC,aAAa,OAAO,YAAY;CACjC,IAAI,MAAM,WAAW,aAAa,WAAW,CAAC,MAAM,iBAAiB,QAAQ,SAAS;EAAE,MAAM;EAAU,MAAM,CAAC,iBAAiB;EAAG,SAAS;CAAyD,CAAC;AACxM,CAAC;AAED,SAAgB,kBAAkB,MAAY,QAAsB;CAElE,IAAI,CAAC;EADmE,KAAK,CAAC,aAAa,KAAK;EAAG,SAAS,CAAC,OAAO,KAAK;EAAG,OAAO,CAAC,OAAO,KAAK;CACrI,EAAE,KAAK,SAAS,CAAC,SAAS,OAAO,aAAa,GAAG,MAAM,IAAI,MAAM,iBAAiB,OAAO,cAAc,2BAA2B,KAAK,UAAU;CAC5J,IAAI,KAAK,YAAY,iBAAiB,OAAO,kBAAkB,OAAO,MAAM,IAAI,MAAM,wCAAwC;CAC9H,IAAI,OAAO,aAAa,KAAK,cAAc,WAAW,MAAM,IAAI,MAAM,8DAA8D;CACpI,IAAI,KAAK,cAAc,aAAa,CAAC,OAAO,WAAW,MAAM,IAAI,MAAM,GAAG,KAAK,UAAU,yEAAyE;CAClK,IAAI,OAAO,WAAW,aAAa,WAAW,CAAC,OAAO,iBAAiB,MAAM,IAAI,MAAM,wDAAwD;CAC/I,IAAI,KAAK,aAAa,YAAY,OAAO,mBAAmB,OAAO,WAAW,aAAa,UAAU,MAAM,IAAI,MAAM,wGAAwG;CAC7N,IAAI,KAAK,cAAc,QAAQ,sBAAsB,QAAQ,KAAK,cAAc;AAClF;AACA,MAAa,cAAc,EAAE,KAAK;CAAC;CAAY;CAAS;CAAa;CAAU;CAAU;AAAS,CAAC;AAEnG,MAAa,eAAe,EAAE,KAAK;CAAC;CAAU;CAAW;CAAW;CAAW;CAAU;CAAS;AAAO,CAAC;AAE1G,MAAa,kBAAkB,EAAE,aAAa;CAAE,QAAQ;CAAM,mBAAmB;CAAM,gBAAgB;CAAM,OAAO,EAAE,QAAQ;AAAE,CAAC;AAEjI,MAAa,wBAAwB,EAAE,aAAa,EAAE,OAAO,KAAK,CAAC;AAEnE,MAAa,wBAAwB,EAAE,aAAa;CAAE,UAAU,EAAE,OAAO,CAAC,CAAC,OAAO;CAAG,UAAU;CAAuB,QAAQ,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,SAAS;CAAG,QAAQ,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,SAAS;AAAE,CAAC;AAEtM,MAAa,qBAAqB,EAAE,aAAa;CAAE,MAAM,EAAE,MAAM,IAAI,CAAC,CAAC,SAAS;CAAG,QAAQ;CAAM,MAAM,KAAK,SAAS;AAAE,CAAC;AAExH,MAAa,iBAAiB,EAAE,aAAa;CAC3C,QAAQ,EAAE,QAAQ;CAAG,cAAc;CAAM,OAAO,EAAE,MAAM,IAAI,CAAC,CAAC,SAAS;CAAG,MAAM,EAAE,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC;AACrG,CAAC;AAED,MAAa,eAAe,EAAE,aAAa;CAAE,UAAU,EAAE,QAAQ;CAAG,UAAU,EAAE,MAAM,IAAI;CAAG,oBAAoB,EAAE,MAAM,EAAE,aAAa;EAAE,aAAa;EAAM,UAAU;CAAK,CAAC,CAAC;AAAE,CAAC;AAEjL,MAAa,iBAAiB,EAAE,aAAa;CAAE,MAAM,EAAE,KAAK;EAAC;EAAiB;EAAO;EAAO;EAAO;EAAa;CAAK,CAAC;CAAG,MAAM;CAAM,QAAQ,KAAK,MAAM,gBAAgB;CAAG,WAAW,EAAE,IAAI,SAAS;AAAE,CAAC;AAExM,MAAa,mBAAmB,EAAE,aAAa;CAC7C,QAAQ;CAAM,QAAQ,KAAK,SAAS;CAAG,QAAQ,EAAE,KAAK,CAAC,YAAY,OAAO,CAAC;CAAG,OAAO,KAAK,SAAS;CACnG,OAAO,EAAE,OAAO,CAAC,CAAC,SAAS;CAAG,YAAY,EAAE,OAAO,CAAC,CAAC,OAAO;AAC9D,CAAC;AAED,MAAa,kBAAkB,EAAE,KAAK;CAAC;CAAY;CAAS;AAAQ,CAAC;AAErE,MAAa,iBAAiB,EAAE,aAAa;CAC3C,IAAI,EAAE,KAAK;CAAG,OAAO;CAAiB,WAAW,EAAE,OAAO,CAAC,CAAC,OAAO;CAAG,eAAe,KAAK,MAAM,gBAAgB;AAClH,CAAC;AAED,MAAa,YAAY,EAAE,OAAO;CAChC,IAAI;CAAM,MAAM;CAAY,aAAa,WAAW,SAAS;CAAG,kBAAkB,KAAK,SAAS,CAAC,CAAC,QAAQ,IAAI;CAAG,QAAQ;CAAc,OAAO;CAAa,QAAQ;CACnK,WAAW,EAAE,OAAO,CAAC,CAAC,OAAO;CAAG,WAAW,EAAE,OAAO,CAAC,CAAC,OAAO;CAAG,QAAQ,EAAE,OAAO,CAAC,CAAC,OAAO;CAC1F,UAAU,EAAE,cAAc,aAAa,KAAK;CAAG,WAAW;CAAO,MAAM,eAAe,SAAS,CAAC,CAAC,SAAS;CAAG,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;CAC7I,UAAU,EAAE,OAAO,EAAE,OAAO,GAAG,qBAAqB,CAAC,CAAC,QAAQ,CAAC,CAAC;CAAG,SAAS;CAAoB,MAAM,EAAE,MAAM,IAAI;CAAG,WAAW;CAChI,WAAW,EAAE,KAAK,CAAC,CAAC,SAAS;CAAG,YAAY,KAAK,MAAM,gBAAgB,CAAC,CAAC,SAAS;CAAG,cAAc,EAAE,QAAQ,CAAC,CAAC,SAAS;CACxH,eAAe,EAAE,QAAQ,IAAI,CAAC,CAAC,SAAS;CACxC,iBAAiB,KAAK,OAAO,YAAY,kCAAkC,CAAC,CAAC,SAAS;CAAG,cAAc,KAAK,MAAM,gBAAgB,CAAC,CAAC,SAAS;CAAG,gBAAgB,EAAE,QAAQ,CAAC,CAAC,SAAS;CAAG,mBAAmB,EAAE,QAAQ,CAAC,CAAC,SAAS;CAChO,eAAe,YAAY,SAAS;CACpC,eAAe,EAAE,aAAa;EAAE,MAAM,EAAE,QAAQ,cAAc;EAAG,OAAO;EAAa,cAAc;CAAmB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS;CAC7I,gBAAgB,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS;CAAG,OAAO,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS;CAAG,UAAU,EAAE,OAAO,CAAC,CAAC,SAAS;CACzH,UAAU,eAAe,SAAS;CAAG,gBAAgB,EAAE,aAAa;EAAE,KAAK,EAAE,IAAI;EAAG,WAAW;EAAM,QAAQ;CAAK,CAAC,CAAC,CAAC,SAAS;CAC9H,WAAW,gBAAgB,SAAS;CAAG,gBAAgB,KAAK,SAAS,CAAC,CAAC,SAAS;CAAG,0BAA0B,KAAK,MAAM,gBAAgB,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS;CAAG,kBAAkB,KAAK,SAAS,CAAC,CAAC,SAAS;CAAG,YAAY,KAAK,SAAS;CACzO,iBAAiB,EAAE,aAAa;EAAE,QAAQ,KAAK,MAAM,gBAAgB;EAAG,MAAM;CAAK,CAAC,CAAC,CAAC,SAAS;CAC/F,mBAAmB,KAAK,MAAM,gBAAgB,CAAC,CAAC,SAAS;CACzD,mBAAmB,EAAE,aAAa;EAAE,cAAc;EAAO,YAAY,EAAE,OAAO,MAAM,KAAK;EAAG,SAAS,EAAE,aAAa;GAAE,WAAW;GAAO,aAAa;EAAM,CAAC;CAAE,CAAC,CAAC,CAAC,SAAS;CAC1K,QAAQ,aAAa,SAAS,CAAC,CAAC,SAAS;CAAG,SAAS,KAAK,SAAS,CAAC,CAAC,SAAS;CAAG,eAAe,KAAK,SAAS,CAAC,CAAC,SAAS;CAAG,UAAU,eAAe,SAAS,CAAC,CAAC,SAAS;CACzK,gBAAgB,EAAE,QAAQ,CAAC,CAAC,SAAS;AACvC,CAAC,CAAC,CAAC,YAAY;AAIf,SAAgB,QAAQ,OAA8B;CACpD,OAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AACjE"}
package/dist/store.js CHANGED
@@ -1,35 +1,71 @@
1
- import { ConfigSchema, InboxEntrySchema, JobSchema, SpecSchema, toError } from "./schemas.js";
1
+ import { ConfigSchema, InboxEntrySchema, JobSchema, SpecSchema, toError, validateJobConfig } from "./schemas.js";
2
+ import { canonicalPath, normalizeIsolationPaths, validateProjectSource } from "./config.js";
2
3
  import { TERMINATION_GRACE_MS, inspectProcessGroup, signalOwnedProcessGroup } from "./process.js";
3
- import { randomUUID } from "node:crypto";
4
- import { join } from "node:path";
4
+ import { dependencyDigest } from "./dependencies.js";
5
+ import { projectDirectory, sourceDigest } from "./workspace.js";
5
6
  import { chmodSync, mkdirSync } from "node:fs";
7
+ import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
6
8
  import { DatabaseSync } from "node:sqlite";
9
+ import { randomUUID } from "node:crypto";
7
10
  // src/store.ts
8
11
  function parseStoredJob(data) {
9
12
  if (typeof data !== "string") throw new Error("Stored task data must be JSON text");
10
13
  return JobSchema.parse(JSON.parse(data));
11
14
  }
15
+ function sameIdentity(left, right) {
16
+ return left.id === right.id && left.platform === right.platform && left.channel === right.channel && left.version === right.version;
17
+ }
18
+ function overlaps(left, right) {
19
+ const contains = (root, path) => {
20
+ const child = relative(root, path);
21
+ return !child || child !== ".." && !child.startsWith(`..${sep}`) && !isAbsolute(child);
22
+ };
23
+ return contains(left, right) || contains(right, left);
24
+ }
25
+ function migrateSpec(value) {
26
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return SpecSchema.parse(value);
27
+ const saved = { ...value };
28
+ const apple = saved.apple && typeof saved.apple === "object" && !Array.isArray(saved.apple) ? { ...saved.apple } : void 0;
29
+ if (saved.platform === void 0) {
30
+ saved.framework = "flutter";
31
+ saved.platform = "ios";
32
+ saved.channel = "app-store";
33
+ }
34
+ if (apple) {
35
+ saved.version ??= apple.version;
36
+ saved.screenshotsDir ??= apple.screenshotsDir;
37
+ delete apple.version;
38
+ delete apple.screenshotsDir;
39
+ saved.apple = apple;
40
+ }
41
+ return SpecSchema.parse(saved);
42
+ }
12
43
  var Store = class {
13
44
  db;
45
+ directory;
14
46
  constructor(directory) {
15
- mkdirSync(directory, {
47
+ this.directory = canonicalPath(directory);
48
+ mkdirSync(this.directory, {
16
49
  recursive: true,
17
50
  mode: 448
18
51
  });
19
- this.db = new DatabaseSync(join(directory, "queue.sqlite"));
20
- chmodSync(join(directory, "queue.sqlite"), 384);
21
- this.db.exec(`PRAGMA busy_timeout=5000; PRAGMA journal_mode=WAL;
22
- CREATE TABLE IF NOT EXISTS jobs (id TEXT PRIMARY KEY, product TEXT NOT NULL, version TEXT NOT NULL, status TEXT NOT NULL, next_at INTEGER NOT NULL, data TEXT NOT NULL, UNIQUE(product, version));
23
- CREATE TABLE IF NOT EXISTS events (seq INTEGER PRIMARY KEY, job TEXT NOT NULL, at INTEGER NOT NULL, data TEXT NOT NULL);
24
- CREATE TABLE IF NOT EXISTS worker (id INTEGER PRIMARY KEY CHECK(id=1), pid INTEGER NOT NULL);
25
- CREATE TABLE IF NOT EXISTS inbox (source TEXT PRIMARY KEY, digest TEXT, data TEXT NOT NULL);`);
52
+ this.db = new DatabaseSync(join(this.directory, "queue.sqlite"));
53
+ chmodSync(join(this.directory, "queue.sqlite"), 384);
54
+ this.db.exec("PRAGMA busy_timeout=5000;");
26
55
  const version = Number(this.db.prepare("PRAGMA user_version").get()?.user_version);
27
- if (version > 2) {
56
+ if (version > 3) {
28
57
  this.db.close();
29
58
  throw new Error(`Queue schema ${version} is newer than this factory supports; use the version that created it. No task data was changed.`);
30
59
  }
31
- if (version < 2) try {
60
+ this.db.exec("PRAGMA journal_mode=WAL;");
61
+ if (version < 3) try {
32
62
  this.transaction(() => {
63
+ const lockedVersion = Number(this.db.prepare("PRAGMA user_version").get()?.user_version);
64
+ if (lockedVersion > 3) throw new Error(`Queue schema ${lockedVersion} is newer than this factory supports; use the version that created it. No task data was changed.`);
65
+ if (lockedVersion === 3) return;
66
+ this.db.exec(`CREATE TABLE IF NOT EXISTS events (seq INTEGER PRIMARY KEY, job TEXT NOT NULL, at INTEGER NOT NULL, data TEXT NOT NULL);
67
+ CREATE TABLE IF NOT EXISTS worker (id INTEGER PRIMARY KEY CHECK(id=1), pid INTEGER NOT NULL);
68
+ CREATE TABLE IF NOT EXISTS inbox (source TEXT PRIMARY KEY, digest TEXT, data TEXT NOT NULL);`);
33
69
  const owner = this.db.prepare("SELECT pid FROM worker WHERE id=1").get();
34
70
  if (owner) {
35
71
  if (typeof owner.pid !== "number" || !Number.isSafeInteger(owner.pid) || owner.pid <= 1) throw new Error("Legacy queue has invalid execution ownership; inspect it with the previous factory version");
@@ -42,32 +78,77 @@ var Store = class {
42
78
  if (alive) throw new Error(`Stop the previous factory execution (${owner.pid}) before migrating its queue; no process was signaled`);
43
79
  }
44
80
  this.db.exec("CREATE TABLE IF NOT EXISTS snapshot_migrations (job TEXT NOT NULL, version INTEGER NOT NULL, data TEXT NOT NULL, PRIMARY KEY(job,version));");
45
- for (const row of this.db.prepare("SELECT id,data FROM jobs").all()) {
81
+ const hasJobs = this.db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='jobs'").get();
82
+ const rows = hasJobs ? this.db.prepare("SELECT id,data FROM jobs ORDER BY rowid").all() : [];
83
+ this.db.exec("CREATE TABLE jobs_v3 (id TEXT PRIMARY KEY, product TEXT NOT NULL, platform TEXT NOT NULL, channel TEXT NOT NULL, version TEXT NOT NULL, status TEXT NOT NULL, next_at INTEGER NOT NULL, data TEXT NOT NULL, UNIQUE(product,platform,channel,version));");
84
+ for (const row of rows) {
46
85
  if (typeof row.data !== "string") throw new Error("Stored task data must be JSON text");
47
86
  const saved = JSON.parse(row.data);
48
- this.db.prepare("INSERT OR IGNORE INTO snapshot_migrations VALUES(?,?,?)").run(row.id, version, row.data);
87
+ if (saved.turn) throw new Error(`Abandon the active host turn for task ${row.id} with the previous factory version before migrating; no host turn was changed`);
88
+ this.db.prepare("INSERT OR IGNORE INTO snapshot_migrations VALUES(?,?,?)").run(row.id, lockedVersion, row.data);
49
89
  if (saved.requestSpec === void 0) {
50
90
  const changed = this.db.prepare("SELECT 1 FROM events WHERE job=? AND json_type(data,'$.spec') IS NOT NULL LIMIT 1").get(row.id);
51
91
  saved.requestSpec = changed ? null : saved.spec;
52
92
  saved.requestSpecError = changed ? "Original intake identity cannot be proven after legacy requirement edits; use reconcile-intake with the original requirements" : null;
53
93
  }
94
+ saved.spec = migrateSpec(saved.spec);
95
+ if (saved.requestSpec !== null) saved.requestSpec = migrateSpec(saved.requestSpec);
54
96
  const config = saved.config;
55
97
  config.executionEnv ??= config.agentEnv ?? [];
56
98
  config.maxHostTurns ??= config.maxAgentCalls ?? 16;
99
+ config.packageTarget ??= saved.spec?.channel === "google-play" ? "aab" : "simulator";
57
100
  for (const key of [
58
101
  "builder",
59
102
  "reviewer",
60
103
  "researcher",
61
104
  "agentEnv",
62
105
  "maxAgentCalls",
63
- "maxConcurrentJobs"
106
+ "maxConcurrentJobs",
107
+ "allowSubmit",
108
+ "allowRelease"
64
109
  ]) delete config[key];
65
110
  saved.hostTurns ??= saved.agentCalls ?? 0;
66
111
  delete saved.agentCalls;
112
+ const advanced = [
113
+ "review",
114
+ "package",
115
+ "publish",
116
+ "monitor"
117
+ ].includes(saved.stage) || [
118
+ "built",
119
+ "ready",
120
+ "approved",
121
+ "released"
122
+ ].includes(saved.status);
123
+ const published = saved.receipt && Object.keys(saved.receipt).length > 0;
124
+ saved.verifiedDigest = null;
125
+ saved.verifiedDependencyDigest = null;
126
+ saved.screenshotDigest = null;
127
+ delete saved.verifiedAt;
128
+ saved.review = null;
129
+ saved.artifact = null;
130
+ if (!published) {
131
+ saved.ipaPath = null;
132
+ saved.archiveDigest = null;
133
+ }
134
+ if (published) {
135
+ saved.stage = ["publish", "monitor"].includes(saved.stage) ? "package" : saved.stage;
136
+ saved.status = "blocked";
137
+ saved.blockedReason = null;
138
+ saved.error = "Automatic store publication has been removed from this tool. Store submission and review are manual human work; legacy publication receipts are retained only as records and will not be replayed.";
139
+ } else if (advanced) {
140
+ saved.stage = "verify";
141
+ saved.status = "blocked";
142
+ saved.blockedReason = null;
143
+ saved.error = "Snapshot fingerprint contract changed; retry to run fresh verification and review before packaging";
144
+ }
67
145
  const job = JobSchema.parse(saved);
68
- this.db.prepare("UPDATE jobs SET data=? WHERE id=?").run(JSON.stringify(job), job.id);
146
+ validateJobConfig(job.spec, job.config);
147
+ if (job.id !== row.id) throw new Error("Stored task ID does not match its queue row");
148
+ this.db.prepare("INSERT INTO jobs_v3 VALUES(?,?,?,?,?,?,?,?)").run(job.id, job.spec.id, job.spec.platform, job.spec.channel, job.spec.version, job.status, job.nextAt, JSON.stringify(job));
69
149
  }
70
- this.db.exec("PRAGMA user_version=2;");
150
+ if (hasJobs) this.db.exec("DROP TABLE jobs;");
151
+ this.db.exec("ALTER TABLE jobs_v3 RENAME TO jobs; PRAGMA user_version=3;");
71
152
  });
72
153
  } catch (cause) {
73
154
  this.db.close();
@@ -98,47 +179,80 @@ var Store = class {
98
179
  }
99
180
  enqueue(specValue, configValue, baseJobId) {
100
181
  const spec = SpecSchema.parse(specValue);
101
- const config = ConfigSchema.parse(configValue);
182
+ const config = this.snapshotConfig(configValue);
183
+ validateJobConfig(spec, config);
102
184
  return this.transaction(() => this.insertJob(spec, config, baseJobId));
103
185
  }
104
186
  checkIngest(specValue, baseJobId, pending = []) {
105
187
  const spec = SpecSchema.parse(specValue);
106
- const row = this.db.prepare("SELECT data FROM jobs WHERE product=? AND version=?").get(spec.id, spec.apple?.version ?? "1.0.0");
188
+ if (baseJobId && spec.project?.sourceDir) throw new Error("project.sourceDir and baseJobId are mutually exclusive");
189
+ const row = this.db.prepare("SELECT data FROM jobs WHERE product=? AND platform=? AND channel=? AND version=?").get(spec.id, spec.platform, spec.channel, spec.version);
107
190
  const existing = row ? parseStoredJob(row.data) : void 0;
108
191
  if (existing?.requestSpec === null) throw new Error(existing.requestSpecError ?? "Original intake identity is unknown; use reconcile-intake with the original requirements");
109
- const prospective = pending.find((item) => item.spec.id === spec.id && (item.spec.apple?.version ?? "1.0.0") === (spec.apple?.version ?? "1.0.0"));
192
+ const prospective = pending.find((item) => sameIdentity(item.spec, spec));
110
193
  const original = existing?.requestSpec ? {
111
194
  spec: existing.requestSpec,
112
- baseJobId: existing.baseJobId
195
+ baseJobId: existing.automaticBase ? void 0 : existing.baseJobId
113
196
  } : void 0;
114
- for (const candidate of [original, prospective]) if (candidate && (JSON.stringify(candidate.spec) !== JSON.stringify(spec) || candidate.baseJobId !== baseJobId)) throw new Error(`${spec.id} already has immutable requirements for this version; submit a new apple.version instead of editing its task`);
115
- if (existing) return existing;
116
- const signature = (value) => `${value.purpose.trim().toLowerCase()}\n${value.audience.trim().toLowerCase()}`;
117
- const duplicates = (job) => job.spec.id !== spec.id && signature(job.spec) === signature(spec);
118
- const duplicate = this.list().find(duplicates) ?? pending.find(duplicates);
119
- if (duplicate) throw new Error(`Product purpose and audience duplicate ${duplicate.spec.id}; differentiate actual utility, not names or code`);
120
- const base = baseJobId === void 0 ? void 0 : this.get(baseJobId);
121
- if (base && (base.spec.id !== spec.id || ![
122
- "built",
123
- "ready",
124
- "approved",
125
- "released"
126
- ].includes(base.status) || !base.verifiedDigest || base.groups.length)) throw new Error("baseJobId must identify a finished, verified task of the same product with no live command groups");
197
+ for (const candidate of [original, prospective]) if (candidate && (JSON.stringify(candidate.spec) !== JSON.stringify(spec) || candidate.baseJobId !== baseJobId)) throw new Error(`${spec.id} already has immutable requirements for this platform, channel and version; submit a new version instead of editing its task`);
198
+ if (existing || prospective) return existing;
199
+ const base = this.selectBase(spec, baseJobId, pending);
200
+ if (base && (base.spec.id !== spec.id || base.spec.platform !== spec.platform || base.spec.channel !== spec.channel || base.spec.framework !== spec.framework || !["built", "ready"].includes(base.status) || !base.verifiedDigest || !base.verifiedDependencyDigest || base.turn || base.groups.length || base.manualRecovery)) throw new Error("baseJobId must identify a finished, verified task of the same product, platform, channel and framework with no active work");
201
+ if (base) this.predecessorSpec(spec, base);
202
+ if (base && sourceDigest(base.workspace, true, base.spec.framework, base.spec.project?.directory) !== base.verifiedDigest) throw new Error("baseJobId source changed after verification; verify the predecessor again before creating a successor");
203
+ if (base && dependencyDigest(base.workspace, base.spec.framework, base.spec.project?.directory) !== base.verifiedDependencyDigest) throw new Error("baseJobId dependency resolution changed after verification; verify the predecessor again before creating a successor");
204
+ }
205
+ selectBase(spec, baseJobId, pending = []) {
206
+ if (baseJobId !== void 0) return this.get(baseJobId);
207
+ if (spec.platform !== "ios" || spec.channel !== "app-store" || spec.project?.sourceDir) return void 0;
208
+ const row = this.db.prepare("SELECT data FROM jobs WHERE product=? AND platform=? AND channel=? AND status IN ('built','ready') ORDER BY rowid DESC LIMIT 1").get(spec.id, spec.platform, spec.channel);
209
+ if (row) return parseStoredJob(row.data);
210
+ if (this.db.prepare("SELECT 1 FROM jobs WHERE product=? AND platform=? AND channel=? LIMIT 1").get(spec.id, spec.platform, spec.channel) || pending.some((item) => item.spec.id === spec.id && item.spec.platform === spec.platform && item.spec.channel === spec.channel && !sameIdentity(item.spec, spec))) throw new Error("Previous App Store versions exist but none is finished and verified; finish or explicitly import the existing application before creating a successor");
211
+ }
212
+ predecessorSpec(spec, base) {
213
+ if (spec.platform !== "ios" || spec.channel !== "app-store") return spec;
214
+ const directory = base.spec.project?.directory ?? ".";
215
+ if (spec.project && spec.project.directory !== directory) throw new Error("project.directory must preserve the predecessor project directory");
216
+ return SpecSchema.parse({
217
+ ...spec,
218
+ ...base.spec.project || spec.project ? { project: { directory } } : {}
219
+ });
220
+ }
221
+ snapshotConfig(value) {
222
+ const config = ConfigSchema.parse(value);
223
+ const root = config.configPath ? dirname(resolve(config.configPath)) : process.cwd();
224
+ config.stateDir = canonicalPath(resolve(root, config.stateDir));
225
+ config.workspaceRoot = canonicalPath(resolve(root, config.workspaceRoot));
226
+ if (overlaps(config.stateDir, config.workspaceRoot) || overlaps(this.directory, config.workspaceRoot)) throw new Error("stateDir and workspaceRoot must not overlap in either direction");
227
+ normalizeIsolationPaths(config);
228
+ return config;
127
229
  }
128
230
  insertJob(spec, config, baseJobId) {
129
- if (this.checkIngest(spec, baseJobId)) throw new Error(`${spec.id} ${spec.apple?.version ?? "1.0.0"} is already queued; retry its task or use a new product version`);
130
- const version = spec.apple?.version ?? "1.0.0";
131
- const base = baseJobId === void 0 ? void 0 : this.get(baseJobId);
231
+ if (this.checkIngest(spec, baseJobId)) throw new Error(`${spec.id} ${spec.platform} ${spec.channel} ${spec.version} is already queued; retry its task or use a new product version`);
232
+ const base = this.selectBase(spec, baseJobId);
233
+ const effectiveSpec = base ? this.predecessorSpec(spec, base) : spec;
234
+ const sourceDirectory = validateProjectSource(spec, config);
235
+ let importDigest;
236
+ if (sourceDirectory) {
237
+ if (overlaps(this.directory, sourceDirectory)) throw new Error("project.sourceDir must not overlap the queue directory in either direction");
238
+ importDigest = sourceDigest(sourceDirectory, true, spec.framework, spec.project?.directory);
239
+ }
132
240
  const now = Date.now();
133
241
  const id = randomUUID();
134
242
  const job = JobSchema.parse({
135
243
  id,
136
- spec,
244
+ spec: effectiveSpec,
137
245
  requestSpec: spec,
138
246
  config,
139
247
  ...base ? {
140
248
  baseJobId: base.id,
141
- baseDigest: base.verifiedDigest
249
+ baseDigest: base.verifiedDigest,
250
+ ...baseJobId === void 0 ? { automaticBase: true } : {}
251
+ } : {},
252
+ ...sourceDirectory ? {
253
+ sourceDirectory,
254
+ importDigest,
255
+ importPrepared: false
142
256
  } : {},
143
257
  stage: "research",
144
258
  status: "queued",
@@ -156,11 +270,12 @@ var Store = class {
156
270
  logs: [],
157
271
  workspace: join(config.workspaceRoot, id)
158
272
  });
159
- this.db.prepare("INSERT INTO jobs VALUES (?,?,?,?,?,?)").run(id, spec.id, version, job.status, now, JSON.stringify(job));
273
+ projectDirectory(job);
274
+ this.db.prepare("INSERT INTO jobs VALUES (?,?,?,?,?,?,?,?)").run(id, spec.id, spec.platform, spec.channel, spec.version, job.status, now, JSON.stringify(job));
160
275
  this.event(id, {
161
276
  stage: job.stage,
162
277
  status: job.status,
163
- baseJobId
278
+ baseJobId: job.baseJobId
164
279
  });
165
280
  return job;
166
281
  }
@@ -188,7 +303,8 @@ var Store = class {
188
303
  }
189
304
  ingest(source, digest, specValue, configValue, baseJobId) {
190
305
  const spec = SpecSchema.parse(specValue);
191
- const config = ConfigSchema.parse(configValue);
306
+ const config = this.snapshotConfig(configValue);
307
+ validateJobConfig(spec, config);
192
308
  return this.transaction(() => {
193
309
  const job = this.checkIngest(spec, baseJobId) ?? this.insertJob(spec, config, baseJobId);
194
310
  return this.saveInbox(InboxEntrySchema.parse({
@@ -214,11 +330,17 @@ var Store = class {
214
330
  });
215
331
  }
216
332
  saveJob(current, patch, reconcileIntake = false) {
333
+ const config = patch.config ? this.snapshotConfig(patch.config) : current.config;
334
+ if (current.sourceDirectory && [config.stateDir, config.workspaceRoot].some((path) => overlaps(path, current.sourceDirectory))) throw new Error("Pinned project source must not overlap stateDir or workspaceRoot after reconfiguration");
217
335
  const job = JobSchema.parse({
218
336
  ...current,
219
337
  ...patch,
338
+ config,
220
339
  updatedAt: Date.now()
221
340
  });
341
+ validateJobConfig(job.spec, job.config);
342
+ if (job.id !== current.id || !sameIdentity(job.spec, current.spec) || job.spec.framework !== current.spec.framework) throw new Error("Task product, platform, channel, version and framework are immutable; enqueue a new task");
343
+ if (job.workspace !== current.workspace || job.sourceDirectory !== current.sourceDirectory || job.importDigest !== current.importDigest || job.baseJobId !== current.baseJobId || job.baseDigest !== current.baseDigest || job.automaticBase !== current.automaticBase || JSON.stringify(job.spec.project) !== JSON.stringify(current.spec.project)) throw new Error("Pinned project source and workspace identity cannot be changed by task updates");
222
344
  if (!reconcileIntake && patch.requestSpec !== void 0 && JSON.stringify(job.requestSpec) !== JSON.stringify(current.requestSpec)) throw new Error("Original intake requirements cannot be changed by task updates");
223
345
  this.db.prepare("UPDATE jobs SET status=?,next_at=?,data=? WHERE id=?").run(job.status, job.nextAt, JSON.stringify(job), job.id);
224
346
  this.event(job.id, patch);
@@ -229,7 +351,7 @@ var Store = class {
229
351
  return this.transaction(() => {
230
352
  const current = this.get(id);
231
353
  if (current.requestSpec !== null) throw new Error("This task already has a known intake identity; it cannot be replaced");
232
- if (spec.id !== current.spec.id || (spec.apple?.version ?? "1.0.0") !== (current.spec.apple?.version ?? "1.0.0") || baseJobId !== current.baseJobId) throw new Error("Original requirements must match this task product, version and predecessor");
354
+ if (!sameIdentity(spec, current.spec) || spec.framework !== current.spec.framework || baseJobId !== (current.automaticBase ? void 0 : current.baseJobId)) throw new Error("Original requirements must match this task product, platform, channel, framework, version and predecessor");
233
355
  return this.saveJob(current, {
234
356
  requestSpec: spec,
235
357
  requestSpecError: null
package/dist/store.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","names":[],"sources":["../src/store.ts"],"sourcesContent":["import { DatabaseSync } from 'node:sqlite';\nimport { mkdirSync, chmodSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { randomUUID } from 'node:crypto';\nimport { inspectProcessGroup, signalOwnedProcessGroup, TERMINATION_GRACE_MS } from './process.js';\nimport { ConfigSchema, JobSchema, SpecSchema, InboxEntrySchema, toError, type CommandMetadata, type Job, type Spec, type Config, type InboxEntry } from './schemas.js';\n\nfunction parseStoredJob(data: unknown): Job {\n if (typeof data !== 'string') throw new Error('Stored task data must be JSON text');\n return JobSchema.parse(JSON.parse(data));\n}\n\nexport class Store {\n readonly db: DatabaseSync;\n\n constructor(directory: string) {\n mkdirSync(directory, { recursive: true, mode: 0o700 });\n this.db = new DatabaseSync(join(directory, 'queue.sqlite'));\n chmodSync(join(directory, 'queue.sqlite'), 0o600);\n this.db.exec(`PRAGMA busy_timeout=5000; PRAGMA journal_mode=WAL;\n CREATE TABLE IF NOT EXISTS jobs (id TEXT PRIMARY KEY, product TEXT NOT NULL, version TEXT NOT NULL, status TEXT NOT NULL, next_at INTEGER NOT NULL, data TEXT NOT NULL, UNIQUE(product, version));\n CREATE TABLE IF NOT EXISTS events (seq INTEGER PRIMARY KEY, job TEXT NOT NULL, at INTEGER NOT NULL, data TEXT NOT NULL);\n CREATE TABLE IF NOT EXISTS worker (id INTEGER PRIMARY KEY CHECK(id=1), pid INTEGER NOT NULL);\n CREATE TABLE IF NOT EXISTS inbox (source TEXT PRIMARY KEY, digest TEXT, data TEXT NOT NULL);`);\n const version = Number(this.db.prepare('PRAGMA user_version').get()?.user_version);\n if (version > 2) { this.db.close(); throw new Error(`Queue schema ${version} is newer than this factory supports; use the version that created it. No task data was changed.`); }\n if (version < 2) {\n try {\n this.transaction(() => {\n const owner = this.db.prepare('SELECT pid FROM worker WHERE id=1').get();\n if (owner) {\n if (typeof owner.pid !== 'number' || !Number.isSafeInteger(owner.pid) || owner.pid <= 1) throw new Error('Legacy queue has invalid execution ownership; inspect it with the previous factory version');\n let alive = true;\n try { process.kill(owner.pid, 0); } catch (cause) { alive = toError(cause).code !== 'ESRCH'; }\n if (alive) throw new Error(`Stop the previous factory execution (${owner.pid}) before migrating its queue; no process was signaled`);\n }\n this.db.exec('CREATE TABLE IF NOT EXISTS snapshot_migrations (job TEXT NOT NULL, version INTEGER NOT NULL, data TEXT NOT NULL, PRIMARY KEY(job,version));');\n for (const row of this.db.prepare('SELECT id,data FROM jobs').all()) {\n if (typeof row.data !== 'string') throw new Error('Stored task data must be JSON text');\n const saved = JSON.parse(row.data);\n this.db.prepare('INSERT OR IGNORE INTO snapshot_migrations VALUES(?,?,?)').run(row.id, version, row.data);\n if (saved.requestSpec === undefined) {\n const changed = this.db.prepare(\"SELECT 1 FROM events WHERE job=? AND json_type(data,'$.spec') IS NOT NULL LIMIT 1\").get(row.id);\n saved.requestSpec = changed ? null : saved.spec;\n saved.requestSpecError = changed ? 'Original intake identity cannot be proven after legacy requirement edits; use reconcile-intake with the original requirements' : null;\n }\n const config = saved.config;\n config.executionEnv ??= config.agentEnv ?? [];\n config.maxHostTurns ??= config.maxAgentCalls ?? 16;\n for (const key of ['builder', 'reviewer', 'researcher', 'agentEnv', 'maxAgentCalls', 'maxConcurrentJobs']) delete config[key];\n saved.hostTurns ??= saved.agentCalls ?? 0;\n delete saved.agentCalls;\n const job = JobSchema.parse(saved);\n this.db.prepare('UPDATE jobs SET data=? WHERE id=?').run(JSON.stringify(job), job.id);\n }\n this.db.exec('PRAGMA user_version=2;');\n });\n } catch (cause) {\n this.db.close();\n throw new Error(`Cannot migrate saved factory snapshots: ${toError(cause).message}. No migration was committed. Preserve queue.sqlite and use the previous factory version to inspect or repair unsupported data.`);\n }\n }\n }\n close(): void { this.db.close(); }\n transaction<T>(action: () => T): T {\n this.db.exec('BEGIN IMMEDIATE');\n try { const result = action(); this.db.exec('COMMIT'); return result; }\n catch (error) { this.db.exec('ROLLBACK'); throw error; }\n }\n list(): Job[] { return this.db.prepare('SELECT data FROM jobs ORDER BY rowid').all().map(row => parseStoredJob(row.data)); }\n get(id: string): Job {\n const row = this.db.prepare('SELECT data FROM jobs WHERE id=?').get(id);\n if (!row) throw new Error(`Unknown task ${id}; use status to list task IDs`);\n return parseStoredJob(row.data);\n }\n enqueue(specValue: unknown, configValue: unknown, baseJobId?: string): Job {\n const spec = SpecSchema.parse(specValue);\n const config = ConfigSchema.parse(configValue);\n return this.transaction(() => this.insertJob(spec, config, baseJobId));\n }\n checkIngest(specValue: unknown, baseJobId?: string, pending: ReadonlyArray<{ spec: Spec; baseJobId?: string }> = []): Job | undefined {\n const spec = SpecSchema.parse(specValue);\n const row = this.db.prepare('SELECT data FROM jobs WHERE product=? AND version=?').get(spec.id, spec.apple?.version ?? '1.0.0');\n const existing = row ? parseStoredJob(row.data) : undefined;\n if (existing?.requestSpec === null) throw new Error(existing.requestSpecError ?? 'Original intake identity is unknown; use reconcile-intake with the original requirements');\n const prospective = pending.find(item => item.spec.id === spec.id && (item.spec.apple?.version ?? '1.0.0') === (spec.apple?.version ?? '1.0.0'));\n const original = existing?.requestSpec ? { spec: existing.requestSpec, baseJobId: existing.baseJobId } : undefined;\n for (const candidate of [original, prospective]) {\n if (candidate && (JSON.stringify(candidate.spec) !== JSON.stringify(spec) || candidate.baseJobId !== baseJobId)) throw new Error(`${spec.id} already has immutable requirements for this version; submit a new apple.version instead of editing its task`);\n }\n if (existing) return existing;\n const signature = (value: { purpose: string; audience: string }): string => `${value.purpose.trim().toLowerCase()}\\n${value.audience.trim().toLowerCase()}`;\n const duplicates = (job: { spec: Spec }): boolean => job.spec.id !== spec.id && signature(job.spec) === signature(spec);\n const duplicate = this.list().find(duplicates) ?? pending.find(duplicates);\n if (duplicate) throw new Error(`Product purpose and audience duplicate ${duplicate.spec.id}; differentiate actual utility, not names or code`);\n const base = baseJobId === undefined ? undefined : this.get(baseJobId);\n if (base && (base.spec.id !== spec.id || !['built', 'ready', 'approved', 'released'].includes(base.status) || !base.verifiedDigest || base.groups.length)) {\n throw new Error('baseJobId must identify a finished, verified task of the same product with no live command groups');\n }\n return undefined;\n }\n private insertJob(spec: Spec, config: Config, baseJobId?: string): Job {\n if (this.checkIngest(spec, baseJobId)) throw new Error(`${spec.id} ${spec.apple?.version ?? '1.0.0'} is already queued; retry its task or use a new product version`);\n const version = spec.apple?.version ?? '1.0.0';\n const base = baseJobId === undefined ? undefined : this.get(baseJobId);\n const now = Date.now();\n const id = randomUUID();\n const job = JobSchema.parse({ id, spec, requestSpec: spec, config, ...(base ? { baseJobId: base.id, baseDigest: base.verifiedDigest } : {}), stage: 'research', status: 'queued', blockedReason: null, manualRecovery: null, createdAt: now, updatedAt: now, nextAt: now, attempts: {}, hostTurns: 0, turn: null, groups: [], commands: {}, receipt: {}, logs: [], workspace: join(config.workspaceRoot, id) });\n this.db.prepare('INSERT INTO jobs VALUES (?,?,?,?,?,?)').run(id, spec.id, version, job.status, now, JSON.stringify(job));\n this.event(id, { stage: job.stage, status: job.status, baseJobId });\n return job;\n }\n inboxEntries(): InboxEntry[] {\n return this.db.prepare('SELECT data FROM inbox ORDER BY source').all().map(row => {\n if (typeof row.data !== 'string') throw new Error('Stored inbox data must be JSON text');\n return InboxEntrySchema.parse(JSON.parse(row.data));\n });\n }\n private saveInbox(entry: InboxEntry): InboxEntry {\n this.db.prepare('INSERT INTO inbox VALUES(?,?,?) ON CONFLICT(source) DO UPDATE SET digest=excluded.digest,data=excluded.data').run(entry.source, entry.digest, JSON.stringify(entry));\n return entry;\n }\n inboxError(source: string, digest: string | null, error: string): InboxEntry {\n const previous = this.inboxEntries().find(entry => entry.source === source);\n if (previous?.status === 'error' && previous.digest === digest && previous.error === error) return previous;\n return this.saveInbox(InboxEntrySchema.parse({ source, digest, status: 'error', jobId: previous?.jobId ?? null, error, observedAt: Date.now() }));\n }\n ingest(source: string, digest: string, specValue: unknown, configValue: unknown, baseJobId?: string): InboxEntry {\n const spec = SpecSchema.parse(specValue);\n const config = ConfigSchema.parse(configValue);\n return this.transaction(() => {\n const job = this.checkIngest(spec, baseJobId) ?? this.insertJob(spec, config, baseJobId);\n return this.saveInbox(InboxEntrySchema.parse({ source, digest, status: 'accepted', jobId: job.id, error: null, observedAt: Date.now() }));\n });\n }\n event(id: string, data: Record<string, unknown>): void { this.db.prepare('INSERT INTO events(job,at,data) VALUES (?,?,?)').run(id, Date.now(), JSON.stringify(data)); }\n update(id: string, patch: Partial<Job>): Job {\n return this.transition(id, () => patch);\n }\n transition(id: string, change: (current: Job) => Partial<Job>): Job {\n return this.transaction(() => {\n const current = this.get(id);\n return this.saveJob(current, change(current));\n });\n }\n private saveJob(current: Job, patch: Partial<Job>, reconcileIntake = false): Job {\n const job = JobSchema.parse({ ...current, ...patch, updatedAt: Date.now() });\n if (!reconcileIntake && patch.requestSpec !== undefined && JSON.stringify(job.requestSpec) !== JSON.stringify(current.requestSpec)) throw new Error('Original intake requirements cannot be changed by task updates');\n this.db.prepare('UPDATE jobs SET status=?,next_at=?,data=? WHERE id=?').run(job.status, job.nextAt, JSON.stringify(job), job.id);\n this.event(job.id, patch);\n return job;\n }\n reconcileIntake(id: string, specValue: unknown, baseJobId?: string): Job {\n const spec = SpecSchema.parse(specValue);\n return this.transaction(() => {\n const current = this.get(id);\n if (current.requestSpec !== null) throw new Error('This task already has a known intake identity; it cannot be replaced');\n if (spec.id !== current.spec.id || (spec.apple?.version ?? '1.0.0') !== (current.spec.apple?.version ?? '1.0.0') || baseJobId !== current.baseJobId) throw new Error('Original requirements must match this task product, version and predecessor');\n return this.saveJob(current, { requestSpec: spec, requestSpecError: null }, true);\n });\n }\n acquire(): void {\n const jobs = this.transaction(() => {\n const jobs = this.list();\n const owner = this.db.prepare('SELECT pid FROM worker WHERE id=1').get();\n if (owner) {\n if (typeof owner.pid !== 'number' || !Number.isSafeInteger(owner.pid) || owner.pid <= 1) throw new Error('Stored worker PID must be a valid process ID');\n let alive = true;\n try { process.kill(owner.pid, 0); } catch (error) { alive = toError(error).code !== 'ESRCH'; }\n if (alive) throw new Error(`Execution ${owner.pid} already owns this queue; wait for its command to finish`);\n }\n this.db.prepare('INSERT OR REPLACE INTO worker VALUES(1,?)').run(process.pid);\n return jobs;\n });\n for (const job of jobs) {\n if (job.status === 'running' && !job.turn) this.update(job.id, { status: 'waiting', blockedReason: null, nextAt: Date.now(), error: 'Execution stopped during a deterministic stage; recover its command groups before resuming' });\n }\n }\n release(): void {\n this.transaction(() => {\n this.db.prepare('DELETE FROM worker WHERE id=1 AND pid=?').run(process.pid);\n });\n }\n next(canClaim: (job: Job) => boolean = () => true, excludeIds: ReadonlySet<string> = new Set()): Job | undefined {\n const now = Date.now();\n const rows = this.db.prepare(\"SELECT data FROM jobs WHERE (status IN ('queued','waiting') OR (status='blocked' AND json_extract(data,'$.blockedReason.kind')='prerequisite')) AND next_at<=? ORDER BY next_at,rowid\").all(now);\n const jobs = rows.map(row => parseStoredJob(row.data));\n for (const job of jobs) {\n if (excludeIds.has(job.id)) continue;\n const groups: number[] = [], commands: Record<string, CommandMetadata> = {};\n let manualRecovery = job.manualRecovery ?? null, pending = false, nextAt = Infinity;\n for (const pid of job.groups) {\n const command = job.commands[pid];\n const ownership = inspectProcessGroup(pid, command?.identity);\n if (ownership === 'gone') continue;\n groups.push(pid);\n const saved = command ? { ...command } : undefined;\n if (saved) commands[pid] = saved;\n if (ownership !== 'owned' || !saved || !Number.isFinite(saved.deadline)) {\n manualRecovery ??= `Command group ${pid} has ${ownership === 'mismatch' ? 'a different process identity' : 'no verifiable ownership and deadline'}. It was not signaled. Inspect this task's recorded command ownership before retrying.`;\n continue;\n }\n try {\n if (!saved.termAt && now < saved.deadline) {\n nextAt = Math.min(nextAt, saved.deadline, now + job.config.pollMs);\n } else if (!saved.termAt) {\n signalOwnedProcessGroup(pid, saved.identity, 'SIGTERM');\n saved.termAt = Date.now();\n nextAt = Math.min(nextAt, saved.termAt + TERMINATION_GRACE_MS);\n } else if (now < saved.termAt + TERMINATION_GRACE_MS) {\n nextAt = Math.min(nextAt, saved.termAt + TERMINATION_GRACE_MS);\n } else if (!saved.killAt) {\n signalOwnedProcessGroup(pid, saved.identity, 'SIGKILL');\n saved.killAt = Date.now();\n nextAt = Math.min(nextAt, saved.killAt + TERMINATION_GRACE_MS);\n } else if (now < saved.killAt + TERMINATION_GRACE_MS) {\n nextAt = Math.min(nextAt, saved.killAt + TERMINATION_GRACE_MS);\n } else {\n manualRecovery ??= `Command group ${pid} is still active after SIGKILL. No replay is allowed. Inspect and stop the recorded group, then retry this task.`;\n continue;\n }\n pending = true;\n } catch (error) { manualRecovery ??= toError(error).message; }\n }\n if (!groups.length && manualRecovery === null) {\n if (job.groups.length) this.update(job.id, { groups: [], commands: {}, recoveryStage: undefined });\n const claimed = this.transaction(() => {\n const current = this.get(job.id);\n const eligible = current.status === 'queued' || current.status === 'waiting' || (current.status === 'blocked' && current.blockedReason?.kind === 'prerequisite');\n if (!eligible || current.turn || current.nextAt > Date.now() || current.groups.length || current.manualRecovery != null || !canClaim(current)) return undefined;\n return this.saveJob(current, { status: 'running', blockedReason: null, manualRecovery: null, groups: [], commands: {}, recoveryStage: undefined });\n });\n if (claimed) return claimed;\n continue;\n }\n if (pending) {\n this.update(job.id, { status: 'waiting', blockedReason: null, manualRecovery, groups, commands, nextAt, error: manualRecovery ?? 'Recovering an owned command group; waiting for its deadline and TERM-to-KILL cleanup before replay' });\n } else {\n this.update(job.id, { status: 'blocked', blockedReason: null, manualRecovery, groups, commands, error: manualRecovery });\n }\n }\n return undefined;\n }\n events(id: string) {\n this.get(id);\n return this.db.prepare('SELECT seq,at,data FROM events WHERE job=? ORDER BY seq').all(id).map(row => {\n if (typeof row.data !== 'string') throw new Error('Stored event data must be JSON text');\n const data: unknown = JSON.parse(row.data);\n return { ...row, data };\n });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"store.js","names":[],"sources":["../src/store.ts"],"sourcesContent":["import { DatabaseSync } from 'node:sqlite';\nimport { mkdirSync, chmodSync } from 'node:fs';\nimport { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';\nimport { randomUUID } from 'node:crypto';\nimport { inspectProcessGroup, signalOwnedProcessGroup, TERMINATION_GRACE_MS } from './process.js';\nimport { canonicalPath, normalizeIsolationPaths, validateProjectSource } from './config.js';\nimport { projectDirectory, sourceDigest } from './workspace.js';\nimport { dependencyDigest } from './dependencies.js';\nimport { ConfigSchema, JobSchema, SpecSchema, InboxEntrySchema, validateJobConfig, toError, type CommandMetadata, type Job, type Spec, type Config, type InboxEntry } from './schemas.js';\n\nfunction parseStoredJob(data: unknown): Job {\n if (typeof data !== 'string') throw new Error('Stored task data must be JSON text');\n return JobSchema.parse(JSON.parse(data));\n}\n\nfunction sameIdentity(left: Spec, right: Spec): boolean {\n return left.id === right.id && left.platform === right.platform && left.channel === right.channel && left.version === right.version;\n}\n\nfunction overlaps(left: string, right: string): boolean {\n const contains = (root: string, path: string): boolean => {\n const child = relative(root, path);\n return !child || (child !== '..' && !child.startsWith(`..${sep}`) && !isAbsolute(child));\n };\n return contains(left, right) || contains(right, left);\n}\n\nfunction migrateSpec(value: unknown): Spec {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return SpecSchema.parse(value);\n const saved = { ...value } as Record<string, unknown>;\n const apple = saved.apple && typeof saved.apple === 'object' && !Array.isArray(saved.apple) ? { ...saved.apple } as Record<string, unknown> : undefined;\n if (saved.platform === undefined) {\n saved.framework = 'flutter';\n saved.platform = 'ios';\n saved.channel = 'app-store';\n }\n if (apple) {\n saved.version ??= apple.version;\n saved.screenshotsDir ??= apple.screenshotsDir;\n delete apple.version;\n delete apple.screenshotsDir;\n saved.apple = apple;\n }\n return SpecSchema.parse(saved);\n}\n\nexport class Store {\n readonly db: DatabaseSync;\n readonly directory: string;\n\n constructor(directory: string) {\n this.directory = canonicalPath(directory);\n mkdirSync(this.directory, { recursive: true, mode: 0o700 });\n this.db = new DatabaseSync(join(this.directory, 'queue.sqlite'));\n chmodSync(join(this.directory, 'queue.sqlite'), 0o600);\n this.db.exec('PRAGMA busy_timeout=5000;');\n const version = Number(this.db.prepare('PRAGMA user_version').get()?.user_version);\n if (version > 3) { this.db.close(); throw new Error(`Queue schema ${version} is newer than this factory supports; use the version that created it. No task data was changed.`); }\n this.db.exec('PRAGMA journal_mode=WAL;');\n if (version < 3) {\n try {\n this.transaction(() => {\n const lockedVersion = Number(this.db.prepare('PRAGMA user_version').get()?.user_version);\n if (lockedVersion > 3) throw new Error(`Queue schema ${lockedVersion} is newer than this factory supports; use the version that created it. No task data was changed.`);\n if (lockedVersion === 3) return;\n this.db.exec(`CREATE TABLE IF NOT EXISTS events (seq INTEGER PRIMARY KEY, job TEXT NOT NULL, at INTEGER NOT NULL, data TEXT NOT NULL);\n CREATE TABLE IF NOT EXISTS worker (id INTEGER PRIMARY KEY CHECK(id=1), pid INTEGER NOT NULL);\n CREATE TABLE IF NOT EXISTS inbox (source TEXT PRIMARY KEY, digest TEXT, data TEXT NOT NULL);`);\n const owner = this.db.prepare('SELECT pid FROM worker WHERE id=1').get();\n if (owner) {\n if (typeof owner.pid !== 'number' || !Number.isSafeInteger(owner.pid) || owner.pid <= 1) throw new Error('Legacy queue has invalid execution ownership; inspect it with the previous factory version');\n let alive = true;\n try { process.kill(owner.pid, 0); } catch (cause) { alive = toError(cause).code !== 'ESRCH'; }\n if (alive) throw new Error(`Stop the previous factory execution (${owner.pid}) before migrating its queue; no process was signaled`);\n }\n this.db.exec('CREATE TABLE IF NOT EXISTS snapshot_migrations (job TEXT NOT NULL, version INTEGER NOT NULL, data TEXT NOT NULL, PRIMARY KEY(job,version));');\n const hasJobs = this.db.prepare(\"SELECT 1 FROM sqlite_master WHERE type='table' AND name='jobs'\").get();\n const rows = hasJobs ? this.db.prepare('SELECT id,data FROM jobs ORDER BY rowid').all() : [];\n this.db.exec('CREATE TABLE jobs_v3 (id TEXT PRIMARY KEY, product TEXT NOT NULL, platform TEXT NOT NULL, channel TEXT NOT NULL, version TEXT NOT NULL, status TEXT NOT NULL, next_at INTEGER NOT NULL, data TEXT NOT NULL, UNIQUE(product,platform,channel,version));');\n for (const row of rows) {\n if (typeof row.data !== 'string') throw new Error('Stored task data must be JSON text');\n const saved = JSON.parse(row.data);\n if (saved.turn) throw new Error(`Abandon the active host turn for task ${row.id} with the previous factory version before migrating; no host turn was changed`);\n this.db.prepare('INSERT OR IGNORE INTO snapshot_migrations VALUES(?,?,?)').run(row.id, lockedVersion, row.data);\n if (saved.requestSpec === undefined) {\n const changed = this.db.prepare(\"SELECT 1 FROM events WHERE job=? AND json_type(data,'$.spec') IS NOT NULL LIMIT 1\").get(row.id);\n saved.requestSpec = changed ? null : saved.spec;\n saved.requestSpecError = changed ? 'Original intake identity cannot be proven after legacy requirement edits; use reconcile-intake with the original requirements' : null;\n }\n saved.spec = migrateSpec(saved.spec);\n if (saved.requestSpec !== null) saved.requestSpec = migrateSpec(saved.requestSpec);\n const config = saved.config;\n config.executionEnv ??= config.agentEnv ?? [];\n config.maxHostTurns ??= config.maxAgentCalls ?? 16;\n config.packageTarget ??= saved.spec?.channel === 'google-play' ? 'aab' : 'simulator';\n for (const key of ['builder', 'reviewer', 'researcher', 'agentEnv', 'maxAgentCalls', 'maxConcurrentJobs', 'allowSubmit', 'allowRelease']) delete config[key];\n saved.hostTurns ??= saved.agentCalls ?? 0;\n delete saved.agentCalls;\n const advanced = ['review', 'package', 'publish', 'monitor'].includes(saved.stage) || ['built', 'ready', 'approved', 'released'].includes(saved.status);\n const published = saved.receipt && Object.keys(saved.receipt).length > 0;\n saved.verifiedDigest = null;\n saved.verifiedDependencyDigest = null;\n saved.screenshotDigest = null;\n delete saved.verifiedAt;\n saved.review = null;\n saved.artifact = null;\n if (!published) { saved.ipaPath = null; saved.archiveDigest = null; }\n if (published) {\n saved.stage = ['publish', 'monitor'].includes(saved.stage) ? 'package' : saved.stage;\n saved.status = 'blocked';\n saved.blockedReason = null;\n saved.error = 'Automatic store publication has been removed from this tool. Store submission and review are manual human work; legacy publication receipts are retained only as records and will not be replayed.';\n } else if (advanced) {\n saved.stage = 'verify';\n saved.status = 'blocked';\n saved.blockedReason = null;\n saved.error = 'Snapshot fingerprint contract changed; retry to run fresh verification and review before packaging';\n }\n const job = JobSchema.parse(saved);\n validateJobConfig(job.spec, job.config);\n if (job.id !== row.id) throw new Error('Stored task ID does not match its queue row');\n this.db.prepare('INSERT INTO jobs_v3 VALUES(?,?,?,?,?,?,?,?)').run(job.id, job.spec.id, job.spec.platform, job.spec.channel, job.spec.version, job.status, job.nextAt, JSON.stringify(job));\n }\n if (hasJobs) this.db.exec('DROP TABLE jobs;');\n this.db.exec('ALTER TABLE jobs_v3 RENAME TO jobs; PRAGMA user_version=3;');\n });\n } catch (cause) {\n this.db.close();\n throw new Error(`Cannot migrate saved factory snapshots: ${toError(cause).message}. No migration was committed. Preserve queue.sqlite and use the previous factory version to inspect or repair unsupported data.`);\n }\n }\n }\n close(): void { this.db.close(); }\n transaction<T>(action: () => T): T {\n this.db.exec('BEGIN IMMEDIATE');\n try { const result = action(); this.db.exec('COMMIT'); return result; }\n catch (error) { this.db.exec('ROLLBACK'); throw error; }\n }\n list(): Job[] { return this.db.prepare('SELECT data FROM jobs ORDER BY rowid').all().map(row => parseStoredJob(row.data)); }\n get(id: string): Job {\n const row = this.db.prepare('SELECT data FROM jobs WHERE id=?').get(id);\n if (!row) throw new Error(`Unknown task ${id}; use status to list task IDs`);\n return parseStoredJob(row.data);\n }\n enqueue(specValue: unknown, configValue: unknown, baseJobId?: string): Job {\n const spec = SpecSchema.parse(specValue);\n const config = this.snapshotConfig(configValue);\n validateJobConfig(spec, config);\n return this.transaction(() => this.insertJob(spec, config, baseJobId));\n }\n checkIngest(specValue: unknown, baseJobId?: string, pending: ReadonlyArray<{ spec: Spec; baseJobId?: string }> = []): Job | undefined {\n const spec = SpecSchema.parse(specValue);\n if (baseJobId && spec.project?.sourceDir) throw new Error('project.sourceDir and baseJobId are mutually exclusive');\n const row = this.db.prepare('SELECT data FROM jobs WHERE product=? AND platform=? AND channel=? AND version=?').get(spec.id, spec.platform, spec.channel, spec.version);\n const existing = row ? parseStoredJob(row.data) : undefined;\n if (existing?.requestSpec === null) throw new Error(existing.requestSpecError ?? 'Original intake identity is unknown; use reconcile-intake with the original requirements');\n const prospective = pending.find(item => sameIdentity(item.spec, spec));\n const original = existing?.requestSpec ? { spec: existing.requestSpec, baseJobId: existing.automaticBase ? undefined : existing.baseJobId } : undefined;\n for (const candidate of [original, prospective]) {\n if (candidate && (JSON.stringify(candidate.spec) !== JSON.stringify(spec) || candidate.baseJobId !== baseJobId)) throw new Error(`${spec.id} already has immutable requirements for this platform, channel and version; submit a new version instead of editing its task`);\n }\n if (existing || prospective) return existing;\n const base = this.selectBase(spec, baseJobId, pending);\n if (base && (base.spec.id !== spec.id || base.spec.platform !== spec.platform || base.spec.channel !== spec.channel || base.spec.framework !== spec.framework || !['built', 'ready'].includes(base.status) || !base.verifiedDigest || !base.verifiedDependencyDigest || base.turn || base.groups.length || base.manualRecovery)) {\n throw new Error('baseJobId must identify a finished, verified task of the same product, platform, channel and framework with no active work');\n }\n if (base) this.predecessorSpec(spec, base);\n if (base && sourceDigest(base.workspace, true, base.spec.framework, base.spec.project?.directory) !== base.verifiedDigest) throw new Error('baseJobId source changed after verification; verify the predecessor again before creating a successor');\n if (base && dependencyDigest(base.workspace, base.spec.framework, base.spec.project?.directory) !== base.verifiedDependencyDigest) throw new Error('baseJobId dependency resolution changed after verification; verify the predecessor again before creating a successor');\n return undefined;\n }\n private selectBase(spec: Spec, baseJobId?: string, pending: ReadonlyArray<{ spec: Spec; baseJobId?: string }> = []): Job | undefined {\n if (baseJobId !== undefined) return this.get(baseJobId);\n if (spec.platform !== 'ios' || spec.channel !== 'app-store' || spec.project?.sourceDir) return undefined;\n const row = this.db.prepare(\"SELECT data FROM jobs WHERE product=? AND platform=? AND channel=? AND status IN ('built','ready') ORDER BY rowid DESC LIMIT 1\").get(spec.id, spec.platform, spec.channel);\n if (row) return parseStoredJob(row.data);\n const history = this.db.prepare('SELECT 1 FROM jobs WHERE product=? AND platform=? AND channel=? LIMIT 1').get(spec.id, spec.platform, spec.channel);\n if (history || pending.some(item => item.spec.id === spec.id && item.spec.platform === spec.platform && item.spec.channel === spec.channel && !sameIdentity(item.spec, spec))) throw new Error('Previous App Store versions exist but none is finished and verified; finish or explicitly import the existing application before creating a successor');\n return undefined;\n }\n\n private predecessorSpec(spec: Spec, base: Job): Spec {\n if (spec.platform !== 'ios' || spec.channel !== 'app-store') return spec;\n const directory = base.spec.project?.directory ?? '.';\n if (spec.project && spec.project.directory !== directory) throw new Error('project.directory must preserve the predecessor project directory');\n return SpecSchema.parse({ ...spec, ...(base.spec.project || spec.project ? { project: { directory } } : {}) });\n }\n private snapshotConfig(value: unknown): Config {\n const config = ConfigSchema.parse(value);\n const root = config.configPath ? dirname(resolve(config.configPath)) : process.cwd();\n config.stateDir = canonicalPath(resolve(root, config.stateDir));\n config.workspaceRoot = canonicalPath(resolve(root, config.workspaceRoot));\n if (overlaps(config.stateDir, config.workspaceRoot) || overlaps(this.directory, config.workspaceRoot)) throw new Error('stateDir and workspaceRoot must not overlap in either direction');\n normalizeIsolationPaths(config);\n return config;\n }\n private insertJob(spec: Spec, config: Config, baseJobId?: string): Job {\n if (this.checkIngest(spec, baseJobId)) throw new Error(`${spec.id} ${spec.platform} ${spec.channel} ${spec.version} is already queued; retry its task or use a new product version`);\n const base = this.selectBase(spec, baseJobId);\n const effectiveSpec = base ? this.predecessorSpec(spec, base) : spec;\n const sourceDirectory = validateProjectSource(spec, config);\n let importDigest: string | undefined;\n if (sourceDirectory) {\n if (overlaps(this.directory, sourceDirectory)) throw new Error('project.sourceDir must not overlap the queue directory in either direction');\n importDigest = sourceDigest(sourceDirectory, true, spec.framework, spec.project?.directory);\n }\n const now = Date.now();\n const id = randomUUID();\n const job = JobSchema.parse({ id, spec: effectiveSpec, requestSpec: spec, config, ...(base ? { baseJobId: base.id, baseDigest: base.verifiedDigest, ...(baseJobId === undefined ? { automaticBase: true } : {}) } : {}), ...(sourceDirectory ? { sourceDirectory, importDigest, importPrepared: false } : {}), stage: 'research', status: 'queued', blockedReason: null, manualRecovery: null, createdAt: now, updatedAt: now, nextAt: now, attempts: {}, hostTurns: 0, turn: null, groups: [], commands: {}, receipt: {}, logs: [], workspace: join(config.workspaceRoot, id) });\n projectDirectory(job);\n this.db.prepare('INSERT INTO jobs VALUES (?,?,?,?,?,?,?,?)').run(id, spec.id, spec.platform, spec.channel, spec.version, job.status, now, JSON.stringify(job));\n this.event(id, { stage: job.stage, status: job.status, baseJobId: job.baseJobId });\n return job;\n }\n inboxEntries(): InboxEntry[] {\n return this.db.prepare('SELECT data FROM inbox ORDER BY source').all().map(row => {\n if (typeof row.data !== 'string') throw new Error('Stored inbox data must be JSON text');\n return InboxEntrySchema.parse(JSON.parse(row.data));\n });\n }\n private saveInbox(entry: InboxEntry): InboxEntry {\n this.db.prepare('INSERT INTO inbox VALUES(?,?,?) ON CONFLICT(source) DO UPDATE SET digest=excluded.digest,data=excluded.data').run(entry.source, entry.digest, JSON.stringify(entry));\n return entry;\n }\n inboxError(source: string, digest: string | null, error: string): InboxEntry {\n const previous = this.inboxEntries().find(entry => entry.source === source);\n if (previous?.status === 'error' && previous.digest === digest && previous.error === error) return previous;\n return this.saveInbox(InboxEntrySchema.parse({ source, digest, status: 'error', jobId: previous?.jobId ?? null, error, observedAt: Date.now() }));\n }\n ingest(source: string, digest: string, specValue: unknown, configValue: unknown, baseJobId?: string): InboxEntry {\n const spec = SpecSchema.parse(specValue);\n const config = this.snapshotConfig(configValue);\n validateJobConfig(spec, config);\n return this.transaction(() => {\n const job = this.checkIngest(spec, baseJobId) ?? this.insertJob(spec, config, baseJobId);\n return this.saveInbox(InboxEntrySchema.parse({ source, digest, status: 'accepted', jobId: job.id, error: null, observedAt: Date.now() }));\n });\n }\n event(id: string, data: Record<string, unknown>): void { this.db.prepare('INSERT INTO events(job,at,data) VALUES (?,?,?)').run(id, Date.now(), JSON.stringify(data)); }\n update(id: string, patch: Partial<Job>): Job {\n return this.transition(id, () => patch);\n }\n transition(id: string, change: (current: Job) => Partial<Job>): Job {\n return this.transaction(() => {\n const current = this.get(id);\n return this.saveJob(current, change(current));\n });\n }\n private saveJob(current: Job, patch: Partial<Job>, reconcileIntake = false): Job {\n const config = patch.config ? this.snapshotConfig(patch.config) : current.config;\n if (current.sourceDirectory && [config.stateDir, config.workspaceRoot].some(path => overlaps(path, current.sourceDirectory!))) throw new Error('Pinned project source must not overlap stateDir or workspaceRoot after reconfiguration');\n const job = JobSchema.parse({ ...current, ...patch, config, updatedAt: Date.now() });\n validateJobConfig(job.spec, job.config);\n if (job.id !== current.id || !sameIdentity(job.spec, current.spec) || job.spec.framework !== current.spec.framework) throw new Error('Task product, platform, channel, version and framework are immutable; enqueue a new task');\n if (job.workspace !== current.workspace || job.sourceDirectory !== current.sourceDirectory || job.importDigest !== current.importDigest || job.baseJobId !== current.baseJobId || job.baseDigest !== current.baseDigest || job.automaticBase !== current.automaticBase || JSON.stringify(job.spec.project) !== JSON.stringify(current.spec.project)) throw new Error('Pinned project source and workspace identity cannot be changed by task updates');\n if (!reconcileIntake && patch.requestSpec !== undefined && JSON.stringify(job.requestSpec) !== JSON.stringify(current.requestSpec)) throw new Error('Original intake requirements cannot be changed by task updates');\n this.db.prepare('UPDATE jobs SET status=?,next_at=?,data=? WHERE id=?').run(job.status, job.nextAt, JSON.stringify(job), job.id);\n this.event(job.id, patch);\n return job;\n }\n reconcileIntake(id: string, specValue: unknown, baseJobId?: string): Job {\n const spec = SpecSchema.parse(specValue);\n return this.transaction(() => {\n const current = this.get(id);\n if (current.requestSpec !== null) throw new Error('This task already has a known intake identity; it cannot be replaced');\n if (!sameIdentity(spec, current.spec) || spec.framework !== current.spec.framework || baseJobId !== (current.automaticBase ? undefined : current.baseJobId)) throw new Error('Original requirements must match this task product, platform, channel, framework, version and predecessor');\n return this.saveJob(current, { requestSpec: spec, requestSpecError: null }, true);\n });\n }\n acquire(): void {\n const jobs = this.transaction(() => {\n const jobs = this.list();\n const owner = this.db.prepare('SELECT pid FROM worker WHERE id=1').get();\n if (owner) {\n if (typeof owner.pid !== 'number' || !Number.isSafeInteger(owner.pid) || owner.pid <= 1) throw new Error('Stored worker PID must be a valid process ID');\n let alive = true;\n try { process.kill(owner.pid, 0); } catch (error) { alive = toError(error).code !== 'ESRCH'; }\n if (alive) throw new Error(`Execution ${owner.pid} already owns this queue; wait for its command to finish`);\n }\n this.db.prepare('INSERT OR REPLACE INTO worker VALUES(1,?)').run(process.pid);\n return jobs;\n });\n for (const job of jobs) {\n if (job.status === 'running' && !job.turn) this.update(job.id, { status: 'waiting', blockedReason: null, nextAt: Date.now(), error: 'Execution stopped during a deterministic stage; recover its command groups before resuming' });\n }\n }\n release(): void {\n this.transaction(() => {\n this.db.prepare('DELETE FROM worker WHERE id=1 AND pid=?').run(process.pid);\n });\n }\n next(canClaim: (job: Job) => boolean = () => true, excludeIds: ReadonlySet<string> = new Set()): Job | undefined {\n const now = Date.now();\n const rows = this.db.prepare(\"SELECT data FROM jobs WHERE (status IN ('queued','waiting') OR (status='blocked' AND json_extract(data,'$.blockedReason.kind')='prerequisite')) AND next_at<=? ORDER BY next_at,rowid\").all(now);\n const jobs = rows.map(row => parseStoredJob(row.data));\n for (const job of jobs) {\n if (excludeIds.has(job.id)) continue;\n const groups: number[] = [], commands: Record<string, CommandMetadata> = {};\n let manualRecovery = job.manualRecovery ?? null, pending = false, nextAt = Infinity;\n for (const pid of job.groups) {\n const command = job.commands[pid];\n const ownership = inspectProcessGroup(pid, command?.identity);\n if (ownership === 'gone') continue;\n groups.push(pid);\n const saved = command ? { ...command } : undefined;\n if (saved) commands[pid] = saved;\n if (ownership !== 'owned' || !saved || !Number.isFinite(saved.deadline)) {\n manualRecovery ??= `Command group ${pid} has ${ownership === 'mismatch' ? 'a different process identity' : 'no verifiable ownership and deadline'}. It was not signaled. Inspect this task's recorded command ownership before retrying.`;\n continue;\n }\n try {\n if (!saved.termAt && now < saved.deadline) {\n nextAt = Math.min(nextAt, saved.deadline, now + job.config.pollMs);\n } else if (!saved.termAt) {\n signalOwnedProcessGroup(pid, saved.identity, 'SIGTERM');\n saved.termAt = Date.now();\n nextAt = Math.min(nextAt, saved.termAt + TERMINATION_GRACE_MS);\n } else if (now < saved.termAt + TERMINATION_GRACE_MS) {\n nextAt = Math.min(nextAt, saved.termAt + TERMINATION_GRACE_MS);\n } else if (!saved.killAt) {\n signalOwnedProcessGroup(pid, saved.identity, 'SIGKILL');\n saved.killAt = Date.now();\n nextAt = Math.min(nextAt, saved.killAt + TERMINATION_GRACE_MS);\n } else if (now < saved.killAt + TERMINATION_GRACE_MS) {\n nextAt = Math.min(nextAt, saved.killAt + TERMINATION_GRACE_MS);\n } else {\n manualRecovery ??= `Command group ${pid} is still active after SIGKILL. No replay is allowed. Inspect and stop the recorded group, then retry this task.`;\n continue;\n }\n pending = true;\n } catch (error) { manualRecovery ??= toError(error).message; }\n }\n if (!groups.length && manualRecovery === null) {\n if (job.groups.length) this.update(job.id, { groups: [], commands: {}, recoveryStage: undefined });\n const claimed = this.transaction(() => {\n const current = this.get(job.id);\n const eligible = current.status === 'queued' || current.status === 'waiting' || (current.status === 'blocked' && current.blockedReason?.kind === 'prerequisite');\n if (!eligible || current.turn || current.nextAt > Date.now() || current.groups.length || current.manualRecovery != null || !canClaim(current)) return undefined;\n return this.saveJob(current, { status: 'running', blockedReason: null, manualRecovery: null, groups: [], commands: {}, recoveryStage: undefined });\n });\n if (claimed) return claimed;\n continue;\n }\n if (pending) {\n this.update(job.id, { status: 'waiting', blockedReason: null, manualRecovery, groups, commands, nextAt, error: manualRecovery ?? 'Recovering an owned command group; waiting for its deadline and TERM-to-KILL cleanup before replay' });\n } else {\n this.update(job.id, { status: 'blocked', blockedReason: null, manualRecovery, groups, commands, error: manualRecovery });\n }\n }\n return undefined;\n }\n events(id: string) {\n this.get(id);\n return this.db.prepare('SELECT seq,at,data FROM events WHERE job=? ORDER BY seq').all(id).map(row => {\n if (typeof row.data !== 'string') throw new Error('Stored event data must be JSON text');\n const data: unknown = JSON.parse(row.data);\n return { ...row, data };\n });\n }\n}\n"],"mappings":"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"}