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.
- package/README.md +50 -182
- package/benchmarks/peer-context-worker.mjs +186 -0
- package/benchmarks/peer-context.mjs +150 -0
- package/dist/apple-types.js.map +1 -1
- package/dist/cli.js +370 -118
- package/dist/cli.js.map +1 -1
- package/dist/config.js +42 -4
- package/dist/config.js.map +1 -1
- package/dist/dependencies.js +455 -16
- package/dist/dependencies.js.map +1 -1
- package/dist/export-options.js +44 -2
- package/dist/export-options.js.map +1 -1
- package/dist/flutter.js +13 -25
- package/dist/flutter.js.map +1 -1
- package/dist/go-dependencies.js +389 -0
- package/dist/go-dependencies.js.map +1 -0
- package/dist/inbox.js +7 -8
- package/dist/inbox.js.map +1 -1
- package/dist/integrations.js +30 -3
- package/dist/integrations.js.map +1 -1
- package/dist/isolation.js +190 -0
- package/dist/isolation.js.map +1 -0
- package/dist/macos.js +57 -0
- package/dist/macos.js.map +1 -0
- package/dist/mygo-build.js +55 -0
- package/dist/mygo-build.js.map +1 -0
- package/dist/policy.js +22 -0
- package/dist/policy.js.map +1 -0
- package/dist/preflight.js +1 -1
- package/dist/process.js +3 -3
- package/dist/project-profiles.js +163 -0
- package/dist/project-profiles.js.map +1 -0
- package/dist/proof.js +33 -0
- package/dist/proof.js.map +1 -0
- package/dist/remote.js +1 -1
- package/dist/runner.js +269 -287
- package/dist/runner.js.map +1 -1
- package/dist/scheduler.js +0 -3
- package/dist/scheduler.js.map +1 -1
- package/dist/schemas.js +156 -44
- package/dist/schemas.js.map +1 -1
- package/dist/store.js +166 -44
- package/dist/store.js.map +1 -1
- package/dist/vendor/obfuscator/dist/cli.js +72 -0
- package/dist/vendor/obfuscator/dist/cli.js.map +1 -0
- package/dist/vendor/obfuscator/dist/code.js +585 -0
- package/dist/vendor/obfuscator/dist/code.js.map +1 -0
- package/dist/vendor/obfuscator/dist/common.js +262 -0
- package/dist/vendor/obfuscator/dist/common.js.map +1 -0
- package/dist/vendor/obfuscator/dist/config.js +123 -0
- package/dist/vendor/obfuscator/dist/config.js.map +1 -0
- package/dist/vendor/obfuscator/dist/content.js +89 -0
- package/dist/vendor/obfuscator/dist/content.js.map +1 -0
- package/dist/vendor/obfuscator/dist/obfuscate.js +93 -0
- package/dist/vendor/obfuscator/dist/obfuscate.js.map +1 -0
- package/dist/vendor/obfuscator/dist/png.js +283 -0
- package/dist/vendor/obfuscator/dist/png.js.map +1 -0
- package/dist/vendor/obfuscator/dist/project.js +159 -0
- package/dist/vendor/obfuscator/dist/project.js.map +1 -0
- package/dist/vendor/obfuscator/dist/resources.js +335 -0
- package/dist/vendor/obfuscator/dist/resources.js.map +1 -0
- package/dist/vendor/obfuscator/package.json +22 -0
- package/dist/workspace.js +113 -0
- package/dist/workspace.js.map +1 -0
- package/evals/control.mjs +73 -0
- package/evals/expectations.mjs +37 -0
- package/evals/host.mjs +51 -0
- package/evals/oracle.mjs +80 -0
- package/evals/policy.mjs +268 -0
- package/examples/android-java.factory.json +18 -0
- package/examples/android-java.json +16 -0
- package/examples/existing-project.factory.json +18 -0
- package/examples/existing-project.json +17 -0
- package/examples/expo.eas.json +17 -0
- package/examples/expo.factory.json +18 -0
- package/examples/expo.json +16 -0
- package/examples/factory.json +0 -2
- package/examples/flutter.factory.json +13 -0
- package/examples/flutter.json +16 -0
- package/examples/luggage.json +5 -3
- package/examples/mygo-web.factory.json +18 -0
- package/examples/mygo-web.json +16 -0
- package/examples/mygo.factory.json +18 -0
- package/examples/mygo.json +16 -0
- package/examples/react-native.factory.json +18 -0
- package/examples/react-native.json +16 -0
- package/examples/runtime/android-java/app/build.gradle +7 -0
- package/examples/runtime/android-java/app/src/main/AndroidManifest.xml +8 -0
- package/examples/runtime/android-java/app/src/main/java/dev/apphx/runtime/MainActivity.java +44 -0
- package/examples/runtime/android-java/sdk-build.mjs +29 -0
- package/examples/runtime/android-java/settings.gradle +2 -0
- package/examples/runtime/android-ui.mjs +70 -0
- package/examples/runtime/mygo-native/go.mod +13 -0
- package/examples/runtime/mygo-native/go.sum +10 -0
- package/examples/runtime/mygo-native/main.go +173 -0
- package/examples/runtime/mygo-native/main_test.go +14 -0
- package/examples/runtime/mygo-native/mygo.json +1 -0
- package/examples/runtime/mygo-web/bun.lock +15 -0
- package/examples/runtime/mygo-web/go.mod +9 -0
- package/examples/runtime/mygo-web/go.sum +8 -0
- package/examples/runtime/mygo-web/index.html +1 -0
- package/examples/runtime/mygo-web/main.go +60 -0
- package/examples/runtime/mygo-web/mygo.config.ts +10 -0
- package/examples/runtime/mygo-web/package.json +1 -0
- package/examples/runtime/mygo-web/src/main.ts +23 -0
- package/examples/runtime/mygo-web/src/mygo.ts +14 -0
- package/examples/swift.factory.json +18 -0
- package/examples/swift.json +16 -0
- package/examples/uni-app.factory.json +18 -0
- package/examples/uni-app.json +16 -0
- package/package.json +12 -5
- package/scripts/bundle-obfuscator.mjs +19 -0
- package/scripts/runtime-smoke.mjs +209 -0
- package/skills/apphx/SKILL.md +94 -17
- package/skills/apphx/resources/google-play-research-result.json +6 -0
- package/skills/apphx/resources/local-research-result.json +6 -0
- package/skills/apphx/resources/requirement.json +5 -8
- package/skills/apphx/resources/research-result.json +5 -14
- package/skills/apphx/resources/review-result.json +1 -1
- package/dist/apple.js +0 -900
- package/dist/apple.js.map +0 -1
package/dist/integrations.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integrations.js","names":[],"sources":["../src/integrations.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport { closeSync, constants, fstatSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, readdirSync, renameSync, rmSync, unlinkSync, writeFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { dirname, isAbsolute, join, parse, resolve, sep } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { z } from 'zod';\n\nconst skill = 'apphx';\nconst marker = '.apphx-install.json';\nconst owner = 'apphx';\nconst bundledDirectory = fileURLToPath(new URL('../skills/apphx/', import.meta.url));\nconst packageFile = fileURLToPath(new URL('../package.json', import.meta.url));\n\nexport const hosts = Object.freeze([\n { id: 'codex', name: 'Codex', invocation: '$apphx', projectDirectory: '.agents/skills', globalDirectory: '~/.agents/skills', source: 'https://developers.openai.com/codex/skills', instructions: 'Use $apphx in Codex CLI or the IDE extension; /skills lists skills. Restart if discovery has not refreshed.' },\n { id: 'claude', name: 'Claude Code', invocation: '/apphx', projectDirectory: '.claude/skills', globalDirectory: '$CLAUDE_CONFIG_DIR/skills (default ~/.claude/skills)', source: 'https://code.claude.com/docs/en/skills', instructions: 'Use /apphx in a local Claude Code session. Start a new session if the skill is not listed. Cloud sessions do not inherit local user skills.' },\n { id: 'cursor', name: 'Cursor', invocation: '/apphx', projectDirectory: '.cursor/skills', globalDirectory: '~/.cursor/skills', source: 'https://cursor.com/docs/skills', instructions: 'In Agent chat, type / and select apphx. Restart or reopen the project if necessary. Global installation is local, not a Cloud Agent sync.' },\n { id: 'pi', name: 'Pi', invocation: '/skill:apphx', projectDirectory: '.pi/skills', globalDirectory: '$PI_CODING_AGENT_DIR/skills (default ~/.pi/agent/skills)', source: 'https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/docs/skills.md', instructions: 'Grant project trust before using project skills, then /reload and /skill:apphx. Stock Pi has no native subagents: use a separate human-started reviewer session, not a spawned coding-agent CLI.' },\n { id: 'gemini', name: 'Gemini CLI', invocation: 'Use the apphx skill', projectDirectory: '.gemini/skills', globalDirectory: '$GEMINI_CLI_HOME/.gemini/skills (default ~/.gemini/skills)', source: 'https://geminicli.com/docs/cli/skills/', instructions: 'Run /skills reload and /skills list, then ask: Use the apphx skill. Gemini calls activate_skill; approve its consent prompt. This is not a /apphx command.' },\n { id: 'opencode', name: 'OpenCode', invocation: 'Use the apphx skill', projectDirectory: '.opencode/skills', globalDirectory: '$XDG_CONFIG_HOME/opencode/skills (default ~/.config/opencode/skills)', source: 'https://opencode.ai/docs/skills/', instructions: 'Ask OpenCode to use apphx; the agent loads it with its native skill({ name: \"apphx\" }) tool. Skill permissions must permit loading. Restart if necessary; no custom slash command is installed.' },\n { id: 'deepseek', name: 'DeepSeek Harness', invocation: 'Use the apphx skill', projectDirectory: '.dsh/skills (nearest Git root, otherwise selected project)', globalDirectory: '$DSH_HOME/skills (default ~/.dsh/skills)', source: 'https://deepseek-harness.github.io/deepseek-harness/en/reference/subsystems/skills', instructions: 'Ask DeepSeek Harness to use apphx; its native skill({ name: \"apphx\" }) tool loads it. Requires the shipped filesystem skill provider and skill tool, with default roots enabled. Custom provider configurations are not modified.' },\n { id: 'hermes', name: 'Hermes Agent', invocation: '/apphx', projectDirectory: '.hermes/skills', globalDirectory: '$HERMES_HOME/skills (default ~/.hermes/skills)', source: 'https://hermes-agent.nousresearch.com/docs/user-guide/features/skills', instructions: 'Use /apphx. Project installation requires a Git root and a separate owner-approved hermes skills trust decision; this installer never grants trust. For non-Git projects or profile-wide cron use --global in the same HERMES_HOME as Hermes. Confirm discovery with hermes skills list; cron must attach the skill and an absolute workdir.' },\n].map(host => Object.freeze(host)));\n\nexport type IntegrationOptions = { project?: string; global?: boolean };\ntype Host = (typeof hosts)[number];\ntype Location = { host: Host; scope: 'project' | 'global'; path: string };\nexport type IntegrationInspection = {\n host: string; name: string; scope: 'project' | 'global'; path: string;\n invocation: string; instructions: string; source: string;\n installed: boolean; status: 'missing' | 'installed' | 'conflict';\n version?: string; current?: boolean; reason?: string;\n};\n\ntype Tree = { files: Record<string, Buffer>; directories: string[] };\nconst digest = (bytes: string | Buffer) => createHash('sha256').update(bytes).digest('hex');\nconst safeRelative = (path: string) => /^[a-zA-Z0-9][a-zA-Z0-9._/-]*$/.test(path) && path.split('/').every(part => part !== '.' && part !== '..' && part.length > 0) && path !== marker;\nconst digestSchema = z.string().regex(/^[a-f0-9]{64}$/);\nconst manifestSchema = z.strictObject({\n format: z.literal(1), owner: z.literal(owner), skill: z.literal(skill), host: z.string(),\n version: z.string().regex(/^\\d+\\.\\d+\\.\\d+(?:-[\\w.-]+)?(?:\\+[\\w.-]+)?$/),\n files: z.record(z.string().refine(safeRelative), digestSchema), metadataDigest: digestSchema,\n});\ntype Manifest = z.infer<typeof manifestSchema>;\n\n// Never follow a symlink, including an existing ancestor of a not-yet-created target.\nfunction assertPath(path: string): void {\n const absolute = resolve(path);\n let current = parse(absolute).root;\n const parts = absolute.slice(current.length).split(sep).filter(Boolean);\n for (let i = 0; i < parts.length; i++) {\n current = join(current, parts[i]!);\n const info = lstatSync(current, { throwIfNoEntry: false });\n if (!info) return;\n if (info.isSymbolicLink()) throw new Error(`Refusing symlink path: ${current}. Select its real directory explicitly.`);\n if (i < parts.length - 1 && !info.isDirectory()) throw new Error(`Not a directory: ${current}`);\n }\n}\n\nfunction readRegular(path: string, packageSource = false): Buffer {\n assertPath(path);\n const descriptor = openSync(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);\n try {\n const info = fstatSync(descriptor);\n if (!info.isFile() || (!packageSource && info.nlink !== 1)) throw new Error(`Refusing non-regular or hard-linked installation file: ${path}`);\n return readFileSync(descriptor);\n } finally { closeSync(descriptor); }\n}\n\nfunction readTree(root: string, packageSource = false): Tree {\n assertPath(root);\n if (!lstatSync(root).isDirectory()) throw new Error(`Not a skill directory: ${root}`);\n const tree: Tree = { files: Object.create(null), directories: [] };\n function visit(directory: string, prefix: string): void {\n for (const name of readdirSync(directory).sort()) {\n const path = join(directory, name);\n const relative = prefix ? `${prefix}/${name}` : name;\n const info = lstatSync(path);\n if (info.isSymbolicLink()) throw new Error(`Refusing symlink installation entry: ${path}`);\n if (info.isDirectory()) { tree.directories.push(relative); visit(path, relative); }\n else tree.files[relative] = readRegular(path, packageSource);\n }\n }\n visit(root, '');\n return tree;\n}\n\nfunction fileDigests(tree: Tree): Record<string, string> {\n return Object.fromEntries(Object.entries(tree.files).filter(([path]) => path !== marker).map(([path, bytes]) => [path, digest(bytes)]));\n}\n\nfunction metadataDigest(value: Omit<Manifest, 'metadataDigest'>): string {\n return digest(JSON.stringify([value.format, value.owner, value.skill, value.host, value.version, Object.entries(value.files).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)]));\n}\n\nfunction directoriesFor(files: Record<string, unknown>): string[] {\n const directories = new Set<string>();\n for (const file of Object.keys(files)) {\n let parent = file.slice(0, file.lastIndexOf('/'));\n if (!file.includes('/')) continue;\n while (parent) { directories.add(parent); parent = parent.includes('/') ? parent.slice(0, parent.lastIndexOf('/')) : ''; }\n }\n return [...directories].sort();\n}\n\nfunction readOwned(path: string, host: string): Manifest {\n const tree = readTree(path);\n if (!tree.files[marker]) throw new Error(`Foreign skill directory has no ${marker}: ${path}`);\n let manifest: Manifest;\n try { manifest = manifestSchema.parse(JSON.parse(tree.files[marker].toString('utf8'))); }\n catch { throw new Error(`Invalid ownership metadata: ${path}`); }\n if (manifest.host !== host || manifest.metadataDigest !== metadataDigest(manifest)) throw new Error(`Foreign or edited ownership metadata: ${path}`);\n if (!manifest.files['SKILL.md'] || JSON.stringify(Object.keys(tree.files).filter(file => file !== marker).sort()) !== JSON.stringify(Object.keys(manifest.files).sort())) throw new Error(`Added or missing files in owned skill: ${path}`);\n for (const [file, expected] of Object.entries(manifest.files)) {\n if (digest(tree.files[file]!) !== expected) throw new Error(`User-edited skill file: ${join(path, file)}`);\n }\n if (JSON.stringify(tree.directories.sort()) !== JSON.stringify(directoriesFor(manifest.files))) throw new Error(`Unowned directories in skill: ${path}`);\n return manifest;\n}\n\nfunction selectedHost(id: string): Host {\n const host = hosts.find(value => value.id === id);\n if (!host) throw new Error(`Unsupported host ${JSON.stringify(id)}. Supported hosts: ${hosts.map(value => value.id).join(', ')}`);\n return host;\n}\n\nfunction environmentDirectory(name: string, fallback: string): string {\n const configured = process.env[name];\n if (!configured) return fallback;\n const expanded = configured === '~' ? homedir() : configured.startsWith('~/') ? join(homedir(), configured.slice(2)) : configured;\n if (!isAbsolute(expanded)) throw new Error(`${name} must be an absolute directory for installation`);\n return resolve(expanded);\n}\n\nfunction location(id: string, options: IntegrationOptions): Location {\n const host = selectedHost(id);\n if (options.global && options.project !== undefined) throw new Error('Choose either project or global installation, not both');\n if (options.global) {\n let directory: string;\n switch (id) {\n case 'claude': directory = join(environmentDirectory('CLAUDE_CONFIG_DIR', join(homedir(), '.claude')), 'skills'); break;\n case 'gemini': directory = join(environmentDirectory('GEMINI_CLI_HOME', homedir()), '.gemini', 'skills'); break;\n case 'pi': directory = join(environmentDirectory('PI_CODING_AGENT_DIR', join(homedir(), '.pi', 'agent')), 'skills'); break;\n case 'opencode': directory = join(environmentDirectory('XDG_CONFIG_HOME', join(homedir(), '.config')), 'opencode', 'skills'); break;\n case 'deepseek': directory = join(environmentDirectory('DSH_HOME', join(homedir(), '.dsh')), 'skills'); break;\n case 'hermes': directory = join(environmentDirectory('HERMES_HOME', join(homedir(), '.hermes')), 'skills'); break;\n default: directory = join(homedir(), host.projectDirectory);\n }\n return { host, scope: 'global', path: join(directory, skill) };\n }\n if (options.project !== undefined && !options.project.trim()) throw new Error('Project directory must not be empty');\n let project = resolve(options.project ?? process.cwd());\n assertPath(project);\n if (!lstatSync(project, { throwIfNoEntry: false })?.isDirectory()) throw new Error(`Project directory does not exist: ${project}`);\n if (id === 'deepseek' || id === 'hermes') {\n let found = false;\n for (let candidate = project; ; candidate = dirname(candidate)) {\n if (lstatSync(join(candidate, '.git'), { throwIfNoEntry: false })) { project = candidate; found = true; break; }\n if (dirname(candidate) === candidate) break;\n }\n if (id === 'hermes' && !found) throw new Error('Hermes project skills require a Git root; use --global for a profile-level installation instead');\n }\n return { host, scope: 'project', path: join(project, id === 'deepseek' ? '.dsh/skills' : host.projectDirectory, skill) };\n}\n\nfunction details(target: Location) {\n return { host: target.host.id, name: target.host.name, scope: target.scope, path: target.path, invocation: target.host.invocation, instructions: target.host.instructions, source: target.host.source };\n}\n\nfunction bundle(host: string): { tree: Tree; manifest: Manifest } {\n // Package stores may hard-link read-only resources; owned installation targets may not.\n const tree = readTree(bundledDirectory, true);\n if (!tree.files['SKILL.md'] || tree.files[marker]) throw new Error('Invalid bundled apphx skill');\n const files = fileDigests(tree);\n if (!Object.keys(files).every(safeRelative) || JSON.stringify(tree.directories.sort()) !== JSON.stringify(directoriesFor(files))) throw new Error('Invalid bundled skill resource paths');\n const version = JSON.parse(readRegular(packageFile, true).toString('utf8')).version;\n const value: Omit<Manifest, 'metadataDigest'> = { format: 1, owner, skill, host, version, files };\n const manifest = manifestSchema.parse({ ...value, metadataDigest: metadataDigest(value) });\n tree.files[marker] = Buffer.from(`${JSON.stringify(manifest, null, 2)}\\n`);\n return { tree, manifest };\n}\n\nexport function inspectIntegration(host: string, options: IntegrationOptions = {}): IntegrationInspection {\n const target = location(host, options);\n const result = details(target);\n try {\n assertPath(target.path);\n if (!lstatSync(target.path, { throwIfNoEntry: false })) return { ...result, installed: false, status: 'missing' };\n const manifest = readOwned(target.path, host);\n const current = bundle(host).manifest.metadataDigest === manifest.metadataDigest;\n return { ...result, installed: true, status: 'installed', version: manifest.version, current };\n } catch (error) {\n return { ...result, installed: false, status: 'conflict', reason: error instanceof Error ? error.message : String(error) };\n }\n}\n\nfunction withLock<T>(target: Location, action: () => T): T {\n const directory = dirname(dirname(target.path));\n assertPath(directory);\n mkdirSync(directory, { recursive: true });\n assertPath(directory);\n const lock = join(directory, '.apphx-install.lock');\n let descriptor: number;\n try { descriptor = openSync(lock, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY | constants.O_NOFOLLOW, 0o600); }\n catch { throw new Error(`Installation is locked at ${lock}; if an earlier installer stopped, inspect it before removing its lock`); }\n try { return action(); }\n finally { closeSync(descriptor); unlinkSync(lock); }\n}\n\nfunction applyIntegration(host: string, options: IntegrationOptions, operation: 'install' | 'update'): IntegrationInspection & { changed: boolean } {\n const target = location(host, options);\n const next = bundle(host);\n assertPath(target.path);\n if (operation === 'update' && !lstatSync(target.path, { throwIfNoEntry: false })) throw new Error(`No installation at ${target.path}; run apphx install ${host} first with the same scope`);\n return withLock(target, () => {\n assertPath(target.path);\n const previous = lstatSync(target.path, { throwIfNoEntry: false }) ? readOwned(target.path, host) : undefined;\n if (operation === 'update' && !previous) throw new Error(`No installation at ${target.path}; run apphx install ${host} first with the same scope`);\n const result = { ...details(target), installed: true, status: 'installed' as const, version: next.manifest.version, current: true };\n if (previous?.metadataDigest === next.manifest.metadataDigest) return { ...result, changed: false };\n if (operation === 'install' && previous) throw new Error(`An older or different workflow is installed at ${target.path}; use apphx update ${host} with the same scope`);\n const parent = dirname(target.path);\n mkdirSync(parent, { recursive: true });\n const staging = mkdtempSync(join(dirname(parent), '.apphx-stage-'));\n const replacement = join(staging, skill);\n const backup = join(staging, 'previous');\n let moved = false;\n try {\n mkdirSync(replacement);\n for (const [file, bytes] of Object.entries(next.tree.files)) {\n const path = join(replacement, file);\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, bytes, { flag: 'wx', mode: 0o644 });\n }\n assertPath(target.path);\n if (previous) {\n if (readOwned(target.path, host).metadataDigest !== previous.metadataDigest) throw new Error('Installation changed during upgrade');\n renameSync(target.path, backup);\n moved = true;\n } else if (lstatSync(target.path, { throwIfNoEntry: false })) throw new Error('Skill appeared during installation; refusing overwrite');\n try { renameSync(replacement, target.path); }\n catch (error) { if (moved) { renameSync(backup, target.path); moved = false; } throw error; }\n if (moved) { readOwned(backup, host); rmSync(backup, { recursive: true }); moved = false; }\n return { ...result, changed: true };\n } finally {\n // Keep a displaced install intact if rollback or cleanup was interrupted.\n if (!moved) rmSync(staging, { recursive: true, force: true });\n }\n });\n}\n\nexport function installIntegration(host: string, options: IntegrationOptions = {}): IntegrationInspection & { changed: boolean } {\n return applyIntegration(host, options, 'install');\n}\n\nexport function updateIntegration(host: string, options: IntegrationOptions = {}): IntegrationInspection & { changed: boolean } {\n return applyIntegration(host, options, 'update');\n}\n\nexport function uninstallIntegration(host: string, options: IntegrationOptions = {}): IntegrationInspection & { changed: boolean } {\n const target = location(host, options);\n assertPath(target.path);\n const result = { ...details(target), installed: false, status: 'missing' as const };\n if (!lstatSync(target.path, { throwIfNoEntry: false })) return { ...result, changed: false };\n return withLock(target, () => {\n assertPath(target.path);\n if (!lstatSync(target.path, { throwIfNoEntry: false })) return { ...result, changed: false };\n readOwned(target.path, host);\n rmSync(target.path, { recursive: true });\n return { ...result, changed: true };\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"integrations.js","names":[],"sources":["../src/integrations.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport { closeSync, constants, fstatSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, readdirSync, renameSync, rmSync, unlinkSync, writeFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { dirname, isAbsolute, join, parse, resolve, sep } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { z } from 'zod';\n\nconst skill = 'apphx';\nconst marker = '.apphx-install.json';\nconst owner = 'apphx';\nconst bundledDirectory = fileURLToPath(new URL('../skills/apphx/', import.meta.url));\nconst packageFile = fileURLToPath(new URL('../package.json', import.meta.url));\n\nexport const hosts = Object.freeze([\n { id: 'codex', name: 'Codex', invocation: '$apphx', projectDirectory: '.agents/skills', globalDirectory: '~/.agents/skills', source: 'https://developers.openai.com/codex/skills', instructions: 'Use $apphx in Codex CLI or the IDE extension; /skills lists skills. Restart if discovery has not refreshed.' },\n { id: 'claude', name: 'Claude Code', invocation: '/apphx', projectDirectory: '.claude/skills', globalDirectory: '$CLAUDE_CONFIG_DIR/skills (default ~/.claude/skills)', source: 'https://code.claude.com/docs/en/skills', instructions: 'Use /apphx in a local Claude Code session. Start a new session if the skill is not listed. Cloud sessions do not inherit local user skills.' },\n { id: 'cursor', name: 'Cursor', invocation: '/apphx', projectDirectory: '.cursor/skills', globalDirectory: '~/.cursor/skills', source: 'https://cursor.com/docs/skills', instructions: 'In Agent chat, type / and select apphx. Restart or reopen the project if necessary. Global installation is local, not a Cloud Agent sync.' },\n { id: 'pi', name: 'Pi', invocation: '/skill:apphx', projectDirectory: '.pi/skills', globalDirectory: '$PI_CODING_AGENT_DIR/skills (default ~/.pi/agent/skills)', source: 'https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/docs/skills.md', instructions: 'Grant project trust before using project skills, then /reload and /skill:apphx. Stock Pi has no native subagents: use a separate human-started reviewer session, not a spawned coding-agent CLI.' },\n { id: 'gemini', name: 'Gemini CLI', invocation: 'Use the apphx skill', projectDirectory: '.gemini/skills', globalDirectory: '$GEMINI_CLI_HOME/.gemini/skills (default ~/.gemini/skills)', source: 'https://geminicli.com/docs/cli/skills/', instructions: 'Run /skills reload and /skills list, then ask: Use the apphx skill. Gemini calls activate_skill; approve its consent prompt. This is not a /apphx command.' },\n { id: 'opencode', name: 'OpenCode', invocation: 'Use the apphx skill', projectDirectory: '.opencode/skills', globalDirectory: '$XDG_CONFIG_HOME/opencode/skills (default ~/.config/opencode/skills)', source: 'https://opencode.ai/docs/skills/', instructions: 'Ask OpenCode to use apphx; the agent loads it with its native skill({ name: \"apphx\" }) tool. Skill permissions must permit loading. Restart if necessary; no custom slash command is installed.' },\n { id: 'deepseek', name: 'DeepSeek Harness', invocation: 'Use the apphx skill', projectDirectory: '.dsh/skills (nearest Git root, otherwise selected project)', globalDirectory: '$DSH_HOME/skills (default ~/.dsh/skills)', source: 'https://deepseek-harness.github.io/deepseek-harness/en/reference/subsystems/skills', instructions: 'Ask DeepSeek Harness to use apphx; its native skill({ name: \"apphx\" }) tool loads it. Requires the shipped filesystem skill provider and skill tool, with default roots enabled. Custom provider configurations are not modified.' },\n { id: 'hermes', name: 'Hermes Agent', invocation: '/apphx', projectDirectory: '.hermes/skills', globalDirectory: '$HERMES_HOME/skills (default ~/.hermes/skills)', source: 'https://hermes-agent.nousresearch.com/docs/user-guide/features/skills', instructions: 'Use /apphx. Project installation requires a Git root and a separate owner-approved hermes skills trust decision; this installer never grants trust. For non-Git projects or profile-wide cron use --global in the same HERMES_HOME as Hermes. Confirm discovery with hermes skills list; cron must attach the skill and an absolute workdir.' },\n { id: 'qoder', name: 'Qoder', invocation: '/apphx', projectDirectory: '.qoder/skills', globalDirectory: '~/.qoder/skills', source: 'https://docs.qoder.com/cli/Skills', instructions: 'Use /apphx manually or let the model auto-trigger the skill. Run /skills reload in a running session to refresh discovery. User-level skills override project-level skills with the same name.' },\n { id: 'oh-my-pi', name: 'Oh My Pi', invocation: '/skill:apphx', projectDirectory: '.omp/skills', globalDirectory: '~/.omp/agent/skills', source: 'https://github.com/can1357/oh-my-pi/blob/main/docs/skills.md', instructions: 'Use /skill:apphx. Skills are discovered one level under skills/ with SKILL.md; description frontmatter is required by the native .omp provider. Restart the session if discovery has not refreshed.' },\n { id: 'dimagent', name: 'DimAgent', invocation: '/skill:apphx', projectDirectory: '.dimcode/skills', globalDirectory: '~/.dimcode/v2/skills', source: 'https://github.com/daguanren21/apple-app-factory', instructions: 'Use /skill:apphx or describe the requirement to trigger the apphx skill. Restart the session after installation so the skill is discovered.' },\n].map(host => Object.freeze(host)));\n\nexport type IntegrationOptions = { project?: string; global?: boolean };\ntype Host = (typeof hosts)[number];\ntype Location = { host: Host; scope: 'project' | 'global'; path: string };\nexport type IntegrationInspection = {\n host: string; name: string; scope: 'project' | 'global'; path: string;\n invocation: string; instructions: string; source: string;\n installed: boolean; status: 'missing' | 'installed' | 'conflict';\n version?: string; current?: boolean; reason?: string;\n};\n\ntype Tree = { files: Record<string, Buffer>; directories: string[] };\nconst digest = (bytes: string | Buffer) => createHash('sha256').update(bytes).digest('hex');\nconst safeRelative = (path: string) => /^[a-zA-Z0-9][a-zA-Z0-9._/-]*$/.test(path) && path.split('/').every(part => part !== '.' && part !== '..' && part.length > 0) && path !== marker;\nconst digestSchema = z.string().regex(/^[a-f0-9]{64}$/);\nconst manifestSchema = z.strictObject({\n format: z.literal(1), owner: z.literal(owner), skill: z.literal(skill), host: z.string(),\n version: z.string().regex(/^\\d+\\.\\d+\\.\\d+(?:-[\\w.-]+)?(?:\\+[\\w.-]+)?$/),\n files: z.record(z.string().refine(safeRelative), digestSchema), metadataDigest: digestSchema,\n});\ntype Manifest = z.infer<typeof manifestSchema>;\n\n// Never follow a symlink, including an existing ancestor of a not-yet-created target.\nfunction assertPath(path: string): void {\n const absolute = resolve(path);\n let current = parse(absolute).root;\n const parts = absolute.slice(current.length).split(sep).filter(Boolean);\n for (let i = 0; i < parts.length; i++) {\n current = join(current, parts[i]!);\n const info = lstatSync(current, { throwIfNoEntry: false });\n if (!info) return;\n if (info.isSymbolicLink()) throw new Error(`Refusing symlink path: ${current}. Select its real directory explicitly.`);\n if (i < parts.length - 1 && !info.isDirectory()) throw new Error(`Not a directory: ${current}`);\n }\n}\n\nfunction readRegular(path: string, packageSource = false): Buffer {\n assertPath(path);\n const descriptor = openSync(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);\n try {\n const info = fstatSync(descriptor);\n if (!info.isFile() || (!packageSource && info.nlink !== 1)) throw new Error(`Refusing non-regular or hard-linked installation file: ${path}`);\n return readFileSync(descriptor);\n } finally { closeSync(descriptor); }\n}\n\nfunction readTree(root: string, packageSource = false): Tree {\n assertPath(root);\n if (!lstatSync(root).isDirectory()) throw new Error(`Not a skill directory: ${root}`);\n const tree: Tree = { files: Object.create(null), directories: [] };\n function visit(directory: string, prefix: string): void {\n for (const name of readdirSync(directory).sort()) {\n const path = join(directory, name);\n const relative = prefix ? `${prefix}/${name}` : name;\n const info = lstatSync(path);\n if (info.isSymbolicLink()) throw new Error(`Refusing symlink installation entry: ${path}`);\n if (info.isDirectory()) { tree.directories.push(relative); visit(path, relative); }\n else tree.files[relative] = readRegular(path, packageSource);\n }\n }\n visit(root, '');\n return tree;\n}\n\nfunction fileDigests(tree: Tree): Record<string, string> {\n return Object.fromEntries(Object.entries(tree.files).filter(([path]) => path !== marker).map(([path, bytes]) => [path, digest(bytes)]));\n}\n\nfunction metadataDigest(value: Omit<Manifest, 'metadataDigest'>): string {\n return digest(JSON.stringify([value.format, value.owner, value.skill, value.host, value.version, Object.entries(value.files).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)]));\n}\n\nfunction directoriesFor(files: Record<string, unknown>): string[] {\n const directories = new Set<string>();\n for (const file of Object.keys(files)) {\n let parent = file.slice(0, file.lastIndexOf('/'));\n if (!file.includes('/')) continue;\n while (parent) { directories.add(parent); parent = parent.includes('/') ? parent.slice(0, parent.lastIndexOf('/')) : ''; }\n }\n return [...directories].sort();\n}\n\nfunction readOwned(path: string, host: string): Manifest {\n const tree = readTree(path);\n if (!tree.files[marker]) throw new Error(`Foreign skill directory has no ${marker}: ${path}`);\n let manifest: Manifest;\n try { manifest = manifestSchema.parse(JSON.parse(tree.files[marker].toString('utf8'))); }\n catch { throw new Error(`Invalid ownership metadata: ${path}`); }\n if (manifest.host !== host || manifest.metadataDigest !== metadataDigest(manifest)) throw new Error(`Foreign or edited ownership metadata: ${path}`);\n if (!manifest.files['SKILL.md'] || JSON.stringify(Object.keys(tree.files).filter(file => file !== marker).sort()) !== JSON.stringify(Object.keys(manifest.files).sort())) throw new Error(`Added or missing files in owned skill: ${path}`);\n for (const [file, expected] of Object.entries(manifest.files)) {\n if (digest(tree.files[file]!) !== expected) throw new Error(`User-edited skill file: ${join(path, file)}`);\n }\n if (JSON.stringify(tree.directories.sort()) !== JSON.stringify(directoriesFor(manifest.files))) throw new Error(`Unowned directories in skill: ${path}`);\n return manifest;\n}\n\nfunction selectedHost(id: string): Host {\n const host = hosts.find(value => value.id === id);\n if (!host) throw new Error(`Unsupported host ${JSON.stringify(id)}. Supported hosts: ${hosts.map(value => value.id).join(', ')}`);\n return host;\n}\n\nfunction environmentDirectory(name: string, fallback: string): string {\n const configured = process.env[name];\n if (!configured) return fallback;\n const expanded = configured === '~' ? homedir() : configured.startsWith('~/') ? join(homedir(), configured.slice(2)) : configured;\n if (!isAbsolute(expanded)) throw new Error(`${name} must be an absolute directory for installation`);\n return resolve(expanded);\n}\n\nfunction location(id: string, options: IntegrationOptions): Location {\n const host = selectedHost(id);\n if (options.global && options.project !== undefined) throw new Error('Choose either project or global installation, not both');\n if (options.global) {\n let directory: string;\n switch (id) {\n case 'claude': directory = join(environmentDirectory('CLAUDE_CONFIG_DIR', join(homedir(), '.claude')), 'skills'); break;\n case 'gemini': directory = join(environmentDirectory('GEMINI_CLI_HOME', homedir()), '.gemini', 'skills'); break;\n case 'pi': directory = join(environmentDirectory('PI_CODING_AGENT_DIR', join(homedir(), '.pi', 'agent')), 'skills'); break;\n case 'opencode': directory = join(environmentDirectory('XDG_CONFIG_HOME', join(homedir(), '.config')), 'opencode', 'skills'); break;\n case 'deepseek': directory = join(environmentDirectory('DSH_HOME', join(homedir(), '.dsh')), 'skills'); break;\n case 'hermes': directory = join(environmentDirectory('HERMES_HOME', join(homedir(), '.hermes')), 'skills'); break;\n default: directory = join(homedir(), host.projectDirectory);\n }\n return { host, scope: 'global', path: join(directory, skill) };\n }\n if (options.project !== undefined && !options.project.trim()) throw new Error('Project directory must not be empty');\n let project = resolve(options.project ?? process.cwd());\n assertPath(project);\n if (!lstatSync(project, { throwIfNoEntry: false })?.isDirectory()) throw new Error(`Project directory does not exist: ${project}`);\n if (id === 'deepseek' || id === 'hermes') {\n let found = false;\n for (let candidate = project; ; candidate = dirname(candidate)) {\n if (lstatSync(join(candidate, '.git'), { throwIfNoEntry: false })) { project = candidate; found = true; break; }\n if (dirname(candidate) === candidate) break;\n }\n if (id === 'hermes' && !found) throw new Error('Hermes project skills require a Git root; use --global for a profile-level installation instead');\n }\n return { host, scope: 'project', path: join(project, id === 'deepseek' ? '.dsh/skills' : host.projectDirectory, skill) };\n}\n\nfunction details(target: Location) {\n return { host: target.host.id, name: target.host.name, scope: target.scope, path: target.path, invocation: target.host.invocation, instructions: target.host.instructions, source: target.host.source };\n}\n\nfunction bundle(host: string): { tree: Tree; manifest: Manifest } {\n // Package stores may hard-link read-only resources; owned installation targets may not.\n const tree = readTree(bundledDirectory, true);\n if (!tree.files['SKILL.md'] || tree.files[marker]) throw new Error('Invalid bundled apphx skill');\n const files = fileDigests(tree);\n if (!Object.keys(files).every(safeRelative) || JSON.stringify(tree.directories.sort()) !== JSON.stringify(directoriesFor(files))) throw new Error('Invalid bundled skill resource paths');\n const version = JSON.parse(readRegular(packageFile, true).toString('utf8')).version;\n const value: Omit<Manifest, 'metadataDigest'> = { format: 1, owner, skill, host, version, files };\n const manifest = manifestSchema.parse({ ...value, metadataDigest: metadataDigest(value) });\n tree.files[marker] = Buffer.from(`${JSON.stringify(manifest, null, 2)}\\n`);\n return { tree, manifest };\n}\n\nexport function inspectIntegration(host: string, options: IntegrationOptions = {}): IntegrationInspection {\n const target = location(host, options);\n const result = details(target);\n try {\n assertPath(target.path);\n if (!lstatSync(target.path, { throwIfNoEntry: false })) return { ...result, installed: false, status: 'missing' };\n const manifest = readOwned(target.path, host);\n const current = bundle(host).manifest.metadataDigest === manifest.metadataDigest;\n return { ...result, installed: true, status: 'installed', version: manifest.version, current };\n } catch (error) {\n return { ...result, installed: false, status: 'conflict', reason: error instanceof Error ? error.message : String(error) };\n }\n}\n\nfunction withLock<T>(target: Location, action: () => T): T {\n const directory = dirname(dirname(target.path));\n assertPath(directory);\n mkdirSync(directory, { recursive: true });\n assertPath(directory);\n const lock = join(directory, '.apphx-install.lock');\n let descriptor: number;\n try { descriptor = openSync(lock, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY | constants.O_NOFOLLOW, 0o600); }\n catch { throw new Error(`Installation is locked at ${lock}; if an earlier installer stopped, inspect it before removing its lock`); }\n try { return action(); }\n finally { closeSync(descriptor); unlinkSync(lock); }\n}\n\nfunction applyIntegration(host: string, options: IntegrationOptions, operation: 'install' | 'update'): IntegrationInspection & { changed: boolean } {\n const target = location(host, options);\n const next = bundle(host);\n assertPath(target.path);\n if (operation === 'update' && !lstatSync(target.path, { throwIfNoEntry: false })) throw new Error(`No installation at ${target.path}; run apphx install ${host} first with the same scope`);\n return withLock(target, () => {\n assertPath(target.path);\n const previous = lstatSync(target.path, { throwIfNoEntry: false }) ? readOwned(target.path, host) : undefined;\n if (operation === 'update' && !previous) throw new Error(`No installation at ${target.path}; run apphx install ${host} first with the same scope`);\n const result = { ...details(target), installed: true, status: 'installed' as const, version: next.manifest.version, current: true };\n if (previous?.metadataDigest === next.manifest.metadataDigest) return { ...result, changed: false };\n if (operation === 'install' && previous) throw new Error(`An older or different workflow is installed at ${target.path}; use apphx update ${host} with the same scope`);\n const parent = dirname(target.path);\n mkdirSync(parent, { recursive: true });\n const staging = mkdtempSync(join(dirname(parent), '.apphx-stage-'));\n const replacement = join(staging, skill);\n const backup = join(staging, 'previous');\n let moved = false;\n try {\n mkdirSync(replacement);\n for (const [file, bytes] of Object.entries(next.tree.files)) {\n const path = join(replacement, file);\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, bytes, { flag: 'wx', mode: 0o644 });\n }\n assertPath(target.path);\n if (previous) {\n if (readOwned(target.path, host).metadataDigest !== previous.metadataDigest) throw new Error('Installation changed during upgrade');\n renameSync(target.path, backup);\n moved = true;\n } else if (lstatSync(target.path, { throwIfNoEntry: false })) throw new Error('Skill appeared during installation; refusing overwrite');\n try { renameSync(replacement, target.path); }\n catch (error) { if (moved) { renameSync(backup, target.path); moved = false; } throw error; }\n if (moved) { readOwned(backup, host); rmSync(backup, { recursive: true }); moved = false; }\n return { ...result, changed: true };\n } finally {\n // Keep a displaced install intact if rollback or cleanup was interrupted.\n if (!moved) rmSync(staging, { recursive: true, force: true });\n }\n });\n}\n\nexport function installIntegration(host: string, options: IntegrationOptions = {}): IntegrationInspection & { changed: boolean } {\n return applyIntegration(host, options, 'install');\n}\n\nexport function updateIntegration(host: string, options: IntegrationOptions = {}): IntegrationInspection & { changed: boolean } {\n return applyIntegration(host, options, 'update');\n}\n\nexport function uninstallIntegration(host: string, options: IntegrationOptions = {}): IntegrationInspection & { changed: boolean } {\n const target = location(host, options);\n assertPath(target.path);\n const result = { ...details(target), installed: false, status: 'missing' as const };\n if (!lstatSync(target.path, { throwIfNoEntry: false })) return { ...result, changed: false };\n return withLock(target, () => {\n assertPath(target.path);\n if (!lstatSync(target.path, { throwIfNoEntry: false })) return { ...result, changed: false };\n readOwned(target.path, host);\n rmSync(target.path, { recursive: true });\n return { ...result, changed: true };\n });\n}\n"],"mappings":";;;;;;;AAOA,MAAM,QAAQ;AACd,MAAM,SAAS;AACf,MAAM,QAAQ;AACd,MAAM,mBAAmB,cAAc,IAAI,IAAI,oBAAoB,YAAY,GAAG,CAAC;AACnF,MAAM,cAAc,cAAc,IAAI,IAAI,mBAAmB,YAAY,GAAG,CAAC;AAE7E,MAAa,QAAQ,OAAO,OAAO;CACjC;EAAE,IAAI;EAAS,MAAM;EAAS,YAAY;EAAU,kBAAkB;EAAkB,iBAAiB;EAAoB,QAAQ;EAA8C,cAAc;CAA8G;CAC/S;EAAE,IAAI;EAAU,MAAM;EAAe,YAAY;EAAU,kBAAkB;EAAkB,iBAAiB;EAAwD,QAAQ;EAA0C,cAAc;CAA8I;CACtX;EAAE,IAAI;EAAU,MAAM;EAAU,YAAY;EAAU,kBAAkB;EAAkB,iBAAiB;EAAoB,QAAQ;EAAkC,cAAc;CAA4I;CACnU;EAAE,IAAI;EAAM,MAAM;EAAM,YAAY;EAAgB,kBAAkB;EAAc,iBAAiB;EAA4D,QAAQ;EAAsF,cAAc;CAAmM;CAChd;EAAE,IAAI;EAAU,MAAM;EAAc,YAAY;EAAuB,kBAAkB;EAAkB,iBAAiB;EAA8D,QAAQ;EAA0C,cAAc;CAA6J;CACvZ;EAAE,IAAI;EAAY,MAAM;EAAY,YAAY;EAAuB,kBAAkB;EAAoB,iBAAiB;EAAwE,QAAQ;EAAoC,cAAc;CAAkM;CAClc;EAAE,IAAI;EAAY,MAAM;EAAoB,YAAY;EAAuB,kBAAkB;EAA8D,iBAAiB;EAA4C,QAAQ;EAAsF,cAAc;CAAoO;CAC5iB;EAAE,IAAI;EAAU,MAAM;EAAgB,YAAY;EAAU,kBAAkB;EAAkB,iBAAiB;EAAkD,QAAQ;EAAyE,cAAc;CAA+U;CACjlB;EAAE,IAAI;EAAS,MAAM;EAAS,YAAY;EAAU,kBAAkB;EAAiB,iBAAiB;EAAmB,QAAQ;EAAqC,cAAc;CAAiM;CACvX;EAAE,IAAI;EAAY,MAAM;EAAY,YAAY;EAAgB,kBAAkB;EAAe,iBAAiB;EAAuB,QAAQ;EAAgE,cAAc;CAAsM;CACra;EAAE,IAAI;EAAY,MAAM;EAAY,YAAY;EAAgB,kBAAkB;EAAmB,iBAAiB;EAAwB,QAAQ;EAAoD,cAAc;CAA8I;AACxW,CAAC,CAAC,KAAI,SAAQ,OAAO,OAAO,IAAI,CAAC,CAAC;AAalC,MAAM,UAAU,UAA2B,WAAW,QAAQ,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,KAAK;AAC1F,MAAM,gBAAgB,SAAiB,gCAAgC,KAAK,IAAI,KAAK,KAAK,MAAM,GAAG,CAAC,CAAC,OAAM,SAAQ,SAAS,OAAO,SAAS,QAAQ,KAAK,SAAS,CAAC,KAAK,SAAS;AACjL,MAAM,eAAe,EAAE,OAAO,CAAC,CAAC,MAAM,gBAAgB;AACtD,MAAM,iBAAiB,EAAE,aAAa;CACpC,QAAQ,EAAE,QAAQ,CAAC;CAAG,OAAO,EAAE,QAAQ,KAAK;CAAG,OAAO,EAAE,QAAQ,KAAK;CAAG,MAAM,EAAE,OAAO;CACvF,SAAS,EAAE,OAAO,CAAC,CAAC,MAAM,4CAA4C;CACtE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,OAAO,YAAY,GAAG,YAAY;CAAG,gBAAgB;AAClF,CAAC;AAID,SAAS,WAAW,MAAoB;CACtC,MAAM,WAAW,QAAQ,IAAI;CAC7B,IAAI,UAAU,MAAM,QAAQ,CAAC,CAAC;CAC9B,MAAM,QAAQ,SAAS,MAAM,QAAQ,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,OAAO,OAAO;CACtE,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACrC,UAAU,KAAK,SAAS,MAAM,EAAG;EACjC,MAAM,OAAO,UAAU,SAAS,EAAE,gBAAgB,MAAM,CAAC;EACzD,IAAI,CAAC,MAAM;EACX,IAAI,KAAK,eAAe,GAAG,MAAM,IAAI,MAAM,0BAA0B,QAAQ,wCAAwC;EACrH,IAAI,IAAI,MAAM,SAAS,KAAK,CAAC,KAAK,YAAY,GAAG,MAAM,IAAI,MAAM,oBAAoB,SAAS;CAChG;AACF;AAEA,SAAS,YAAY,MAAc,gBAAgB,OAAe;CAChE,WAAW,IAAI;CACf,MAAM,aAAa,SAAS,MAAM,UAAU,WAAW,UAAU,aAAa,UAAU,UAAU;CAClG,IAAI;EACF,MAAM,OAAO,UAAU,UAAU;EACjC,IAAI,CAAC,KAAK,OAAO,KAAM,CAAC,iBAAiB,KAAK,UAAU,GAAI,MAAM,IAAI,MAAM,0DAA0D,MAAM;EAC5I,OAAO,aAAa,UAAU;CAChC,UAAU;EAAE,UAAU,UAAU;CAAG;AACrC;AAEA,SAAS,SAAS,MAAc,gBAAgB,OAAa;CAC3D,WAAW,IAAI;CACf,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC,YAAY,GAAG,MAAM,IAAI,MAAM,0BAA0B,MAAM;CACpF,MAAM,OAAa;EAAE,OAAO,OAAO,OAAO,IAAI;EAAG,aAAa,CAAC;CAAE;CACjE,SAAS,MAAM,WAAmB,QAAsB;EACtD,KAAK,MAAM,QAAQ,YAAY,SAAS,CAAC,CAAC,KAAK,GAAG;GAChD,MAAM,OAAO,KAAK,WAAW,IAAI;GACjC,MAAM,WAAW,SAAS,GAAG,OAAO,GAAG,SAAS;GAChD,MAAM,OAAO,UAAU,IAAI;GAC3B,IAAI,KAAK,eAAe,GAAG,MAAM,IAAI,MAAM,wCAAwC,MAAM;GACzF,IAAI,KAAK,YAAY,GAAG;IAAE,KAAK,YAAY,KAAK,QAAQ;IAAG,MAAM,MAAM,QAAQ;GAAG,OAC7E,KAAK,MAAM,YAAY,YAAY,MAAM,aAAa;EAC7D;CACF;CACA,MAAM,MAAM,EAAE;CACd,OAAO;AACT;AAEA,SAAS,YAAY,MAAoC;CACvD,OAAO,OAAO,YAAY,OAAO,QAAQ,KAAK,KAAK,CAAC,CAAC,QAAQ,CAAC,UAAU,SAAS,MAAM,CAAC,CAAC,KAAK,CAAC,MAAM,WAAW,CAAC,MAAM,OAAO,KAAK,CAAC,CAAC,CAAC;AACxI;AAEA,SAAS,eAAe,OAAiD;CACvE,OAAO,OAAO,KAAK,UAAU;EAAC,MAAM;EAAQ,MAAM;EAAO,MAAM;EAAO,MAAM;EAAM,MAAM;EAAS,OAAO,QAAQ,MAAM,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC;CAAC,CAAC,CAAC;AAC/K;AAEA,SAAS,eAAe,OAA0C;CAChE,MAAM,8BAAc,IAAI,IAAY;CACpC,KAAK,MAAM,QAAQ,OAAO,KAAK,KAAK,GAAG;EACrC,IAAI,SAAS,KAAK,MAAM,GAAG,KAAK,YAAY,GAAG,CAAC;EAChD,IAAI,CAAC,KAAK,SAAS,GAAG,GAAG;EACzB,OAAO,QAAQ;GAAE,YAAY,IAAI,MAAM;GAAG,SAAS,OAAO,SAAS,GAAG,IAAI,OAAO,MAAM,GAAG,OAAO,YAAY,GAAG,CAAC,IAAI;EAAI;CAC3H;CACA,OAAO,CAAC,GAAG,WAAW,CAAC,CAAC,KAAK;AAC/B;AAEA,SAAS,UAAU,MAAc,MAAwB;CACvD,MAAM,OAAO,SAAS,IAAI;CAC1B,IAAI,CAAC,KAAK,MAAM,SAAS,MAAM,IAAI,MAAM,kCAAkC,OAAO,IAAI,MAAM;CAC5F,IAAI;CACJ,IAAI;EAAE,WAAW,eAAe,MAAM,KAAK,MAAM,KAAK,MAAM,OAAO,CAAC,SAAS,MAAM,CAAC,CAAC;CAAG,QAClF;EAAE,MAAM,IAAI,MAAM,+BAA+B,MAAM;CAAG;CAChE,IAAI,SAAS,SAAS,QAAQ,SAAS,mBAAmB,eAAe,QAAQ,GAAG,MAAM,IAAI,MAAM,yCAAyC,MAAM;CACnJ,IAAI,CAAC,SAAS,MAAM,eAAe,KAAK,UAAU,OAAO,KAAK,KAAK,KAAK,CAAC,CAAC,QAAO,SAAQ,SAAS,MAAM,CAAC,CAAC,KAAK,CAAC,MAAM,KAAK,UAAU,OAAO,KAAK,SAAS,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,MAAM,IAAI,MAAM,0CAA0C,MAAM;CAC1O,KAAK,MAAM,CAAC,MAAM,aAAa,OAAO,QAAQ,SAAS,KAAK,GAC1D,IAAI,OAAO,KAAK,MAAM,KAAM,MAAM,UAAU,MAAM,IAAI,MAAM,2BAA2B,KAAK,MAAM,IAAI,GAAG;CAE3G,IAAI,KAAK,UAAU,KAAK,YAAY,KAAK,CAAC,MAAM,KAAK,UAAU,eAAe,SAAS,KAAK,CAAC,GAAG,MAAM,IAAI,MAAM,iCAAiC,MAAM;CACvJ,OAAO;AACT;AAEA,SAAS,aAAa,IAAkB;CACtC,MAAM,OAAO,MAAM,MAAK,UAAS,MAAM,OAAO,EAAE;CAChD,IAAI,CAAC,MAAM,MAAM,IAAI,MAAM,oBAAoB,KAAK,UAAU,EAAE,EAAE,qBAAqB,MAAM,KAAI,UAAS,MAAM,EAAE,CAAC,CAAC,KAAK,IAAI,GAAG;CAChI,OAAO;AACT;AAEA,SAAS,qBAAqB,MAAc,UAA0B;CACpE,MAAM,aAAa,QAAQ,IAAI;CAC/B,IAAI,CAAC,YAAY,OAAO;CACxB,MAAM,WAAW,eAAe,MAAM,QAAQ,IAAI,WAAW,WAAW,IAAI,IAAI,KAAK,QAAQ,GAAG,WAAW,MAAM,CAAC,CAAC,IAAI;CACvH,IAAI,CAAC,WAAW,QAAQ,GAAG,MAAM,IAAI,MAAM,GAAG,KAAK,gDAAgD;CACnG,OAAO,QAAQ,QAAQ;AACzB;AAEA,SAAS,SAAS,IAAY,SAAuC;CACnE,MAAM,OAAO,aAAa,EAAE;CAC5B,IAAI,QAAQ,UAAU,QAAQ,YAAY,KAAA,GAAW,MAAM,IAAI,MAAM,wDAAwD;CAC7H,IAAI,QAAQ,QAAQ;EAClB,IAAI;EACJ,QAAQ,IAAR;GACE,KAAK;IAAU,YAAY,KAAK,qBAAqB,qBAAqB,KAAK,QAAQ,GAAG,SAAS,CAAC,GAAG,QAAQ;IAAG;GAClH,KAAK;IAAU,YAAY,KAAK,qBAAqB,mBAAmB,QAAQ,CAAC,GAAG,WAAW,QAAQ;IAAG;GAC1G,KAAK;IAAM,YAAY,KAAK,qBAAqB,uBAAuB,KAAK,QAAQ,GAAG,OAAO,OAAO,CAAC,GAAG,QAAQ;IAAG;GACrH,KAAK;IAAY,YAAY,KAAK,qBAAqB,mBAAmB,KAAK,QAAQ,GAAG,SAAS,CAAC,GAAG,YAAY,QAAQ;IAAG;GAC9H,KAAK;IAAY,YAAY,KAAK,qBAAqB,YAAY,KAAK,QAAQ,GAAG,MAAM,CAAC,GAAG,QAAQ;IAAG;GACxG,KAAK;IAAU,YAAY,KAAK,qBAAqB,eAAe,KAAK,QAAQ,GAAG,SAAS,CAAC,GAAG,QAAQ;IAAG;GAC5G,SAAS,YAAY,KAAK,QAAQ,GAAG,KAAK,gBAAgB;EAC5D;EACA,OAAO;GAAE;GAAM,OAAO;GAAU,MAAM,KAAK,WAAW,KAAK;EAAE;CAC/D;CACA,IAAI,QAAQ,YAAY,KAAA,KAAa,CAAC,QAAQ,QAAQ,KAAK,GAAG,MAAM,IAAI,MAAM,qCAAqC;CACnH,IAAI,UAAU,QAAQ,QAAQ,WAAW,QAAQ,IAAI,CAAC;CACtD,WAAW,OAAO;CAClB,IAAI,CAAC,UAAU,SAAS,EAAE,gBAAgB,MAAM,CAAC,CAAC,EAAE,YAAY,GAAG,MAAM,IAAI,MAAM,qCAAqC,SAAS;CACjI,IAAI,OAAO,cAAc,OAAO,UAAU;EACxC,IAAI,QAAQ;EACZ,KAAK,IAAI,YAAY,UAAW,YAAY,QAAQ,SAAS,GAAG;GAC9D,IAAI,UAAU,KAAK,WAAW,MAAM,GAAG,EAAE,gBAAgB,MAAM,CAAC,GAAG;IAAE,UAAU;IAAW,QAAQ;IAAM;GAAO;GAC/G,IAAI,QAAQ,SAAS,MAAM,WAAW;EACxC;EACA,IAAI,OAAO,YAAY,CAAC,OAAO,MAAM,IAAI,MAAM,iGAAiG;CAClJ;CACA,OAAO;EAAE;EAAM,OAAO;EAAW,MAAM,KAAK,SAAS,OAAO,aAAa,gBAAgB,KAAK,kBAAkB,KAAK;CAAE;AACzH;AAEA,SAAS,QAAQ,QAAkB;CACjC,OAAO;EAAE,MAAM,OAAO,KAAK;EAAI,MAAM,OAAO,KAAK;EAAM,OAAO,OAAO;EAAO,MAAM,OAAO;EAAM,YAAY,OAAO,KAAK;EAAY,cAAc,OAAO,KAAK;EAAc,QAAQ,OAAO,KAAK;CAAO;AACxM;AAEA,SAAS,OAAO,MAAkD;CAEhE,MAAM,OAAO,SAAS,kBAAkB,IAAI;CAC5C,IAAI,CAAC,KAAK,MAAM,eAAe,KAAK,MAAM,SAAS,MAAM,IAAI,MAAM,6BAA6B;CAChG,MAAM,QAAQ,YAAY,IAAI;CAC9B,IAAI,CAAC,OAAO,KAAK,KAAK,CAAC,CAAC,MAAM,YAAY,KAAK,KAAK,UAAU,KAAK,YAAY,KAAK,CAAC,MAAM,KAAK,UAAU,eAAe,KAAK,CAAC,GAAG,MAAM,IAAI,MAAM,sCAAsC;CACxL,MAAM,UAAU,KAAK,MAAM,YAAY,aAAa,IAAI,CAAC,CAAC,SAAS,MAAM,CAAC,CAAC,CAAC;CAC5E,MAAM,QAA0C;EAAE,QAAQ;EAAG;EAAO;EAAO;EAAM;EAAS;CAAM;CAChG,MAAM,WAAW,eAAe,MAAM;EAAE,GAAG;EAAO,gBAAgB,eAAe,KAAK;CAAE,CAAC;CACzF,KAAK,MAAM,UAAU,OAAO,KAAK,GAAG,KAAK,UAAU,UAAU,MAAM,CAAC,EAAE,GAAG;CACzE,OAAO;EAAE;EAAM;CAAS;AAC1B;AAEA,SAAgB,mBAAmB,MAAc,UAA8B,CAAC,GAA0B;CACxG,MAAM,SAAS,SAAS,MAAM,OAAO;CACrC,MAAM,SAAS,QAAQ,MAAM;CAC7B,IAAI;EACF,WAAW,OAAO,IAAI;EACtB,IAAI,CAAC,UAAU,OAAO,MAAM,EAAE,gBAAgB,MAAM,CAAC,GAAG,OAAO;GAAE,GAAG;GAAQ,WAAW;GAAO,QAAQ;EAAU;EAChH,MAAM,WAAW,UAAU,OAAO,MAAM,IAAI;EAC5C,MAAM,UAAU,OAAO,IAAI,CAAC,CAAC,SAAS,mBAAmB,SAAS;EAClE,OAAO;GAAE,GAAG;GAAQ,WAAW;GAAM,QAAQ;GAAa,SAAS,SAAS;GAAS;EAAQ;CAC/F,SAAS,OAAO;EACd,OAAO;GAAE,GAAG;GAAQ,WAAW;GAAO,QAAQ;GAAY,QAAQ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;EAAE;CAC3H;AACF;AAEA,SAAS,SAAY,QAAkB,QAAoB;CACzD,MAAM,YAAY,QAAQ,QAAQ,OAAO,IAAI,CAAC;CAC9C,WAAW,SAAS;CACpB,UAAU,WAAW,EAAE,WAAW,KAAK,CAAC;CACxC,WAAW,SAAS;CACpB,MAAM,OAAO,KAAK,WAAW,qBAAqB;CAClD,IAAI;CACJ,IAAI;EAAE,aAAa,SAAS,MAAM,UAAU,UAAU,UAAU,SAAS,UAAU,WAAW,UAAU,YAAY,GAAK;CAAG,QACtH;EAAE,MAAM,IAAI,MAAM,6BAA6B,KAAK,uEAAuE;CAAG;CACpI,IAAI;EAAE,OAAO,OAAO;CAAG,UACf;EAAE,UAAU,UAAU;EAAG,WAAW,IAAI;CAAG;AACrD;AAEA,SAAS,iBAAiB,MAAc,SAA6B,WAA+E;CAClJ,MAAM,SAAS,SAAS,MAAM,OAAO;CACrC,MAAM,OAAO,OAAO,IAAI;CACxB,WAAW,OAAO,IAAI;CACtB,IAAI,cAAc,YAAY,CAAC,UAAU,OAAO,MAAM,EAAE,gBAAgB,MAAM,CAAC,GAAG,MAAM,IAAI,MAAM,sBAAsB,OAAO,KAAK,sBAAsB,KAAK,2BAA2B;CAC1L,OAAO,SAAS,cAAc;EAC5B,WAAW,OAAO,IAAI;EACtB,MAAM,WAAW,UAAU,OAAO,MAAM,EAAE,gBAAgB,MAAM,CAAC,IAAI,UAAU,OAAO,MAAM,IAAI,IAAI,KAAA;EACpG,IAAI,cAAc,YAAY,CAAC,UAAU,MAAM,IAAI,MAAM,sBAAsB,OAAO,KAAK,sBAAsB,KAAK,2BAA2B;EACjJ,MAAM,SAAS;GAAE,GAAG,QAAQ,MAAM;GAAG,WAAW;GAAM,QAAQ;GAAsB,SAAS,KAAK,SAAS;GAAS,SAAS;EAAK;EAClI,IAAI,UAAU,mBAAmB,KAAK,SAAS,gBAAgB,OAAO;GAAE,GAAG;GAAQ,SAAS;EAAM;EAClG,IAAI,cAAc,aAAa,UAAU,MAAM,IAAI,MAAM,kDAAkD,OAAO,KAAK,qBAAqB,KAAK,qBAAqB;EACtK,MAAM,SAAS,QAAQ,OAAO,IAAI;EAClC,UAAU,QAAQ,EAAE,WAAW,KAAK,CAAC;EACrC,MAAM,UAAU,YAAY,KAAK,QAAQ,MAAM,GAAG,eAAe,CAAC;EAClE,MAAM,cAAc,KAAK,SAAS,KAAK;EACvC,MAAM,SAAS,KAAK,SAAS,UAAU;EACvC,IAAI,QAAQ;EACZ,IAAI;GACF,UAAU,WAAW;GACrB,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,KAAK,KAAK,KAAK,GAAG;IAC3D,MAAM,OAAO,KAAK,aAAa,IAAI;IACnC,UAAU,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;IAC5C,cAAc,MAAM,OAAO;KAAE,MAAM;KAAM,MAAM;IAAM,CAAC;GACxD;GACA,WAAW,OAAO,IAAI;GACtB,IAAI,UAAU;IACZ,IAAI,UAAU,OAAO,MAAM,IAAI,CAAC,CAAC,mBAAmB,SAAS,gBAAgB,MAAM,IAAI,MAAM,qCAAqC;IAClI,WAAW,OAAO,MAAM,MAAM;IAC9B,QAAQ;GACV,OAAO,IAAI,UAAU,OAAO,MAAM,EAAE,gBAAgB,MAAM,CAAC,GAAG,MAAM,IAAI,MAAM,wDAAwD;GACtI,IAAI;IAAE,WAAW,aAAa,OAAO,IAAI;GAAG,SACrC,OAAO;IAAE,IAAI,OAAO;KAAE,WAAW,QAAQ,OAAO,IAAI;KAAG,QAAQ;IAAO;IAAE,MAAM;GAAO;GAC5F,IAAI,OAAO;IAAE,UAAU,QAAQ,IAAI;IAAG,OAAO,QAAQ,EAAE,WAAW,KAAK,CAAC;IAAG,QAAQ;GAAO;GAC1F,OAAO;IAAE,GAAG;IAAQ,SAAS;GAAK;EACpC,UAAU;GAER,IAAI,CAAC,OAAO,OAAO,SAAS;IAAE,WAAW;IAAM,OAAO;GAAK,CAAC;EAC9D;CACF,CAAC;AACH;AAEA,SAAgB,mBAAmB,MAAc,UAA8B,CAAC,GAAiD;CAC/H,OAAO,iBAAiB,MAAM,SAAS,SAAS;AAClD;AAEA,SAAgB,kBAAkB,MAAc,UAA8B,CAAC,GAAiD;CAC9H,OAAO,iBAAiB,MAAM,SAAS,QAAQ;AACjD;AAEA,SAAgB,qBAAqB,MAAc,UAA8B,CAAC,GAAiD;CACjI,MAAM,SAAS,SAAS,MAAM,OAAO;CACrC,WAAW,OAAO,IAAI;CACtB,MAAM,SAAS;EAAE,GAAG,QAAQ,MAAM;EAAG,WAAW;EAAO,QAAQ;CAAmB;CAClF,IAAI,CAAC,UAAU,OAAO,MAAM,EAAE,gBAAgB,MAAM,CAAC,GAAG,OAAO;EAAE,GAAG;EAAQ,SAAS;CAAM;CAC3F,OAAO,SAAS,cAAc;EAC5B,WAAW,OAAO,IAAI;EACtB,IAAI,CAAC,UAAU,OAAO,MAAM,EAAE,gBAAgB,MAAM,CAAC,GAAG,OAAO;GAAE,GAAG;GAAQ,SAAS;EAAM;EAC3F,UAAU,OAAO,MAAM,IAAI;EAC3B,OAAO,OAAO,MAAM,EAAE,WAAW,KAAK,CAAC;EACvC,OAAO;GAAE,GAAG;GAAQ,SAAS;EAAK;CACpC,CAAC;AACH"}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { canonicalPath, normalizeIsolationPaths } from "./config.js";
|
|
2
|
+
import { accessSync, constants, existsSync, lstatSync, mkdirSync, mkdtempSync, realpathSync } from "node:fs";
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import { delimiter, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
5
|
+
// src/isolation.ts
|
|
6
|
+
function failure(message) {
|
|
7
|
+
return Object.assign(new Error(message), { code: "SETUP_REQUIRED" });
|
|
8
|
+
}
|
|
9
|
+
function contains(parent, child) {
|
|
10
|
+
const path = relative(parent, child);
|
|
11
|
+
return path === "" || path !== ".." && !path.startsWith(`..${sep}`) && !isAbsolute(path);
|
|
12
|
+
}
|
|
13
|
+
function executablePath(command, cwd, env) {
|
|
14
|
+
const candidates = command.includes("/") ? [resolve(cwd, command)] : (env.PATH ?? "").split(delimiter).filter(Boolean).map((path) => resolve(cwd, path, command));
|
|
15
|
+
for (const candidate of candidates) try {
|
|
16
|
+
accessSync(candidate, constants.X_OK);
|
|
17
|
+
const path = realpathSync(candidate);
|
|
18
|
+
if (lstatSync(path).isFile()) return path;
|
|
19
|
+
} catch {}
|
|
20
|
+
throw failure(`Cannot resolve isolated executable ${command}; select an existing executable in the approved worker PATH`);
|
|
21
|
+
}
|
|
22
|
+
function pathRule(path) {
|
|
23
|
+
const info = lstatSync(path);
|
|
24
|
+
if (info.isSymbolicLink() || !info.isFile() && !info.isDirectory()) throw failure(`Isolation grant requires a regular canonical file or directory: ${path}`);
|
|
25
|
+
return `(${info.isDirectory() ? "subpath" : "literal"} ${JSON.stringify(path)})`;
|
|
26
|
+
}
|
|
27
|
+
/** Keep deletion authority on the original trusted root, never a re-resolved target or its parent. */
|
|
28
|
+
function removeConfinedPath(path, root) {
|
|
29
|
+
if (process.platform !== "darwin") throw failure("Confined cleanup requires the selected macOS backend");
|
|
30
|
+
const boundary = resolve(root), target = resolve(path);
|
|
31
|
+
if (boundary === "/" || !contains(boundary, target)) throw failure("Cleanup target must stay within its original isolation root");
|
|
32
|
+
const ancestors = /* @__PURE__ */ new Set([
|
|
33
|
+
"/",
|
|
34
|
+
"/bin",
|
|
35
|
+
"/usr",
|
|
36
|
+
"/System"
|
|
37
|
+
]);
|
|
38
|
+
for (let parent = dirname(boundary); !ancestors.has(parent); parent = dirname(parent)) ancestors.add(parent);
|
|
39
|
+
const profile = [
|
|
40
|
+
"(version 1)",
|
|
41
|
+
"(deny default)",
|
|
42
|
+
"(allow process-exec)",
|
|
43
|
+
"(allow sysctl-read)",
|
|
44
|
+
"(allow file-read* (literal \"/\") (literal \"/bin/rm\") (subpath \"/System/Library\") (subpath \"/usr/lib\") (literal \"/dev/null\"))",
|
|
45
|
+
`(allow file-read-metadata ${[...ancestors].map((parent) => `(literal ${JSON.stringify(parent)})`).join(" ")})`,
|
|
46
|
+
`(allow file-read* file-write* (subpath ${JSON.stringify(boundary)}))`,
|
|
47
|
+
"(allow file-write* (literal \"/dev/null\"))"
|
|
48
|
+
].join("\n");
|
|
49
|
+
const result = spawnSync("/usr/bin/sandbox-exec", [
|
|
50
|
+
"-p",
|
|
51
|
+
profile,
|
|
52
|
+
"/bin/rm",
|
|
53
|
+
"-rf",
|
|
54
|
+
"--",
|
|
55
|
+
target
|
|
56
|
+
], {
|
|
57
|
+
cwd: "/",
|
|
58
|
+
env: {
|
|
59
|
+
PATH: "/usr/bin:/bin",
|
|
60
|
+
LANG: "C"
|
|
61
|
+
},
|
|
62
|
+
encoding: "utf8",
|
|
63
|
+
timeout: 1e4,
|
|
64
|
+
maxBuffer: 65536
|
|
65
|
+
});
|
|
66
|
+
if (result.error || result.signal || result.status !== 0) throw Object.assign(/* @__PURE__ */ new Error(`Confined cleanup did not complete; outside paths were not granted: ${result.error?.message ?? result.stderr ?? result.signal}`), { code: "PROCESS_CLEANUP_BLOCKED" });
|
|
67
|
+
}
|
|
68
|
+
/** This confines spawned commands, not the host agent, resource consumption or GUI/device services. */
|
|
69
|
+
function prepareIsolatedCommand(argv, context) {
|
|
70
|
+
if (!context.config.isolation) throw failure("No subprocess isolation mode was selected");
|
|
71
|
+
if (process.platform !== "darwin") throw failure("macos-sandbox isolation requires macOS; execution was not downgraded to trusted mode");
|
|
72
|
+
try {
|
|
73
|
+
accessSync("/usr/bin/sandbox-exec", constants.X_OK);
|
|
74
|
+
} catch {
|
|
75
|
+
throw failure("The selected macos-sandbox backend is unavailable; no command was started");
|
|
76
|
+
}
|
|
77
|
+
const config = {
|
|
78
|
+
...context.config,
|
|
79
|
+
isolation: {
|
|
80
|
+
...context.config.isolation,
|
|
81
|
+
readOnlyPaths: [...context.config.isolation.readOnlyPaths]
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
normalizeIsolationPaths(config);
|
|
85
|
+
for (let index = 0; index < config.isolation.readOnlyPaths.length; index++) if (config.isolation.readOnlyPaths[index] !== context.config.isolation.readOnlyPaths[index]) throw failure("An isolation read-only path changed identity; review the owner configuration before retrying");
|
|
86
|
+
const workspaceInfo = lstatSync(context.workspace);
|
|
87
|
+
if (!workspaceInfo.isDirectory() || workspaceInfo.isSymbolicLink()) throw failure("Isolation requires a real owned workspace directory");
|
|
88
|
+
const workspace = realpathSync(context.workspace);
|
|
89
|
+
const state = canonicalPath(config.stateDir), workspaceRoot = canonicalPath(config.workspaceRoot);
|
|
90
|
+
if (workspace === workspaceRoot || contains(workspace, state) || contains(state, workspace)) throw failure("Isolation workspace must not expose control state or the whole workspace root");
|
|
91
|
+
const cwd = realpathSync(context.cwd);
|
|
92
|
+
if (![workspace, ...config.isolation.readOnlyPaths].some((path) => contains(path, cwd))) throw failure("Isolated command cwd must stay in its workspace or an approved read-only root");
|
|
93
|
+
const executable = executablePath(argv[0] ?? "", cwd, context.env);
|
|
94
|
+
if (contains(state, executable) || contains(workspaceRoot, executable) && !contains(workspace, executable)) throw failure("An isolated command cannot execute control state or another task workspace");
|
|
95
|
+
const readable = [
|
|
96
|
+
...config.isolation.readOnlyPaths,
|
|
97
|
+
executable,
|
|
98
|
+
realpathSync(process.execPath)
|
|
99
|
+
];
|
|
100
|
+
for (const file of context.readableFiles ?? []) {
|
|
101
|
+
if (!lstatSync(file).isFile() || lstatSync(file).isSymbolicLink()) throw failure(`Harness isolation capability requires a regular file: ${file}`);
|
|
102
|
+
readable.push(realpathSync(file));
|
|
103
|
+
}
|
|
104
|
+
const extraWritable = (context.writableDirectories ?? []).map((path) => {
|
|
105
|
+
if (!lstatSync(path).isDirectory() || lstatSync(path).isSymbolicLink()) throw failure(`Harness isolation capability requires a real directory: ${path}`);
|
|
106
|
+
return realpathSync(path);
|
|
107
|
+
});
|
|
108
|
+
const scratchParent = context.scratchParent ?? state;
|
|
109
|
+
mkdirSync(scratchParent, {
|
|
110
|
+
recursive: true,
|
|
111
|
+
mode: 448
|
|
112
|
+
});
|
|
113
|
+
const scratch = realpathSync(mkdtempSync(join(scratchParent, "isolated-command-")));
|
|
114
|
+
const cleanup = () => removeConfinedPath(scratch, scratch);
|
|
115
|
+
try {
|
|
116
|
+
const home = join(scratch, "home"), temporary = join(scratch, "tmp"), cache = join(scratch, "cache");
|
|
117
|
+
for (const path of [
|
|
118
|
+
home,
|
|
119
|
+
temporary,
|
|
120
|
+
cache
|
|
121
|
+
]) mkdirSync(path, { mode: 448 });
|
|
122
|
+
const env = {
|
|
123
|
+
...context.env,
|
|
124
|
+
HOME: home,
|
|
125
|
+
TMPDIR: `${temporary}${sep}`,
|
|
126
|
+
TMP: temporary,
|
|
127
|
+
TEMP: temporary,
|
|
128
|
+
XDG_CACHE_HOME: cache,
|
|
129
|
+
GRADLE_USER_HOME: join(cache, "gradle"),
|
|
130
|
+
npm_config_cache: join(cache, "npm")
|
|
131
|
+
};
|
|
132
|
+
for (const [name, fallback] of [["GOCACHE", join(cache, "go-build")], ["GOMODCACHE", join(cache, "go-mod")]]) if (!env[name] || contains(state, canonicalPath(resolve(cwd, env[name])))) env[name] = fallback;
|
|
133
|
+
const systemPaths = ["/System/Library", "/usr/lib"].filter(existsSync);
|
|
134
|
+
const devices = [
|
|
135
|
+
"/dev/urandom",
|
|
136
|
+
"/dev/random",
|
|
137
|
+
"/dev/null"
|
|
138
|
+
].filter(existsSync);
|
|
139
|
+
const readablePaths = [.../* @__PURE__ */ new Set([...systemPaths, ...readable])];
|
|
140
|
+
const writablePaths = [.../* @__PURE__ */ new Set([
|
|
141
|
+
workspace,
|
|
142
|
+
scratch,
|
|
143
|
+
...extraWritable
|
|
144
|
+
])];
|
|
145
|
+
const ancestors = /* @__PURE__ */ new Set(["/"]);
|
|
146
|
+
for (const path of [
|
|
147
|
+
...readablePaths,
|
|
148
|
+
...writablePaths,
|
|
149
|
+
...devices,
|
|
150
|
+
cwd
|
|
151
|
+
]) {
|
|
152
|
+
let parent = dirname(path);
|
|
153
|
+
while (true) {
|
|
154
|
+
ancestors.add(parent);
|
|
155
|
+
const next = dirname(parent);
|
|
156
|
+
if (next === parent) break;
|
|
157
|
+
parent = next;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
argv: [
|
|
162
|
+
"/usr/bin/sandbox-exec",
|
|
163
|
+
"-p",
|
|
164
|
+
[
|
|
165
|
+
"(version 1)",
|
|
166
|
+
"(deny default)",
|
|
167
|
+
"(allow process-exec)",
|
|
168
|
+
"(allow process-fork)",
|
|
169
|
+
"(allow sysctl-read)",
|
|
170
|
+
"(allow file-read* (literal \"/\"))",
|
|
171
|
+
`(allow file-read-metadata ${[...ancestors].map((path) => `(literal ${JSON.stringify(path)})`).join(" ")})`,
|
|
172
|
+
`(allow file-read* ${readablePaths.map(pathRule).join(" ")} ${devices.map((path) => `(literal ${JSON.stringify(path)})`).join(" ")})`,
|
|
173
|
+
`(allow file-read* file-write* ${writablePaths.map(pathRule).join(" ")})`,
|
|
174
|
+
"(allow file-write* (literal \"/dev/null\"))",
|
|
175
|
+
...config.isolation.network === "allow" ? ["(allow network*)"] : []
|
|
176
|
+
].join("\n"),
|
|
177
|
+
executable,
|
|
178
|
+
...argv.slice(1)
|
|
179
|
+
],
|
|
180
|
+
env,
|
|
181
|
+
cleanup
|
|
182
|
+
};
|
|
183
|
+
} catch (cause) {
|
|
184
|
+
cleanup();
|
|
185
|
+
throw cause;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
export { prepareIsolatedCommand, removeConfinedPath };
|
|
189
|
+
|
|
190
|
+
//# sourceMappingURL=isolation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"isolation.js","names":[],"sources":["../src/isolation.ts"],"sourcesContent":["import { accessSync, constants, existsSync, lstatSync, mkdirSync, mkdtempSync, realpathSync } from 'node:fs';\nimport { spawnSync } from 'node:child_process';\nimport { delimiter, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';\nimport { canonicalPath, normalizeIsolationPaths } from './config.js';\nimport type { Config } from './schemas.js';\n\nexport interface IsolationCapabilities {\n /** Narrow capabilities for trusted harness utilities; never populated from project command data. */\n readableFiles?: readonly string[];\n writableDirectories?: readonly string[];\n}\n\nexport interface IsolationContext extends IsolationCapabilities {\n config: Config;\n workspace: string;\n cwd: string;\n env: NodeJS.ProcessEnv;\n scratchParent?: string;\n}\n\nexport interface IsolatedCommand {\n argv: string[];\n env: NodeJS.ProcessEnv;\n cleanup(): void;\n}\n\nfunction failure(message: string): Error & { code: string } {\n return Object.assign(new Error(message), { code: 'SETUP_REQUIRED' });\n}\n\nfunction contains(parent: string, child: string): boolean {\n const path = relative(parent, child);\n return path === '' || path !== '..' && !path.startsWith(`..${sep}`) && !isAbsolute(path);\n}\n\nfunction executablePath(command: string, cwd: string, env: NodeJS.ProcessEnv): string {\n const candidates = command.includes('/') ? [resolve(cwd, command)] : (env.PATH ?? '').split(delimiter).filter(Boolean).map(path => resolve(cwd, path, command));\n for (const candidate of candidates) {\n try {\n accessSync(candidate, constants.X_OK);\n const path = realpathSync(candidate);\n if (lstatSync(path).isFile()) return path;\n } catch {}\n }\n throw failure(`Cannot resolve isolated executable ${command}; select an existing executable in the approved worker PATH`);\n}\n\nfunction pathRule(path: string): string {\n const info = lstatSync(path);\n if (info.isSymbolicLink() || !info.isFile() && !info.isDirectory()) throw failure(`Isolation grant requires a regular canonical file or directory: ${path}`);\n return `(${info.isDirectory() ? 'subpath' : 'literal'} ${JSON.stringify(path)})`;\n}\n\n/** Keep deletion authority on the original trusted root, never a re-resolved target or its parent. */\nexport function removeConfinedPath(path: string, root: string): void {\n if (process.platform !== 'darwin') throw failure('Confined cleanup requires the selected macOS backend');\n const boundary = resolve(root), target = resolve(path);\n if (boundary === '/' || !contains(boundary, target)) throw failure('Cleanup target must stay within its original isolation root');\n const ancestors = new Set<string>(['/', '/bin', '/usr', '/System']);\n for (let parent = dirname(boundary); !ancestors.has(parent); parent = dirname(parent)) ancestors.add(parent);\n const profile = [\n '(version 1)', '(deny default)', '(allow process-exec)', '(allow sysctl-read)',\n '(allow file-read* (literal \"/\") (literal \"/bin/rm\") (subpath \"/System/Library\") (subpath \"/usr/lib\") (literal \"/dev/null\"))',\n `(allow file-read-metadata ${[...ancestors].map(parent => `(literal ${JSON.stringify(parent)})`).join(' ')})`,\n `(allow file-read* file-write* (subpath ${JSON.stringify(boundary)}))`,\n '(allow file-write* (literal \"/dev/null\"))',\n ].join('\\n');\n const result = spawnSync('/usr/bin/sandbox-exec', ['-p', profile, '/bin/rm', '-rf', '--', target], {\n cwd: '/', env: { PATH: '/usr/bin:/bin', LANG: 'C' }, encoding: 'utf8', timeout: 10000, maxBuffer: 65536,\n });\n if (result.error || result.signal || result.status !== 0) throw Object.assign(new Error(`Confined cleanup did not complete; outside paths were not granted: ${result.error?.message ?? result.stderr ?? result.signal}`), { code: 'PROCESS_CLEANUP_BLOCKED' });\n}\n\n/** This confines spawned commands, not the host agent, resource consumption or GUI/device services. */\nexport function prepareIsolatedCommand(argv: string[], context: IsolationContext): IsolatedCommand {\n if (!context.config.isolation) throw failure('No subprocess isolation mode was selected');\n if (process.platform !== 'darwin') throw failure('macos-sandbox isolation requires macOS; execution was not downgraded to trusted mode');\n try { accessSync('/usr/bin/sandbox-exec', constants.X_OK); }\n catch { throw failure('The selected macos-sandbox backend is unavailable; no command was started'); }\n const config = { ...context.config, isolation: { ...context.config.isolation, readOnlyPaths: [...context.config.isolation.readOnlyPaths] } };\n normalizeIsolationPaths(config);\n for (let index = 0; index < config.isolation.readOnlyPaths.length; index++) {\n if (config.isolation.readOnlyPaths[index] !== context.config.isolation.readOnlyPaths[index]) throw failure('An isolation read-only path changed identity; review the owner configuration before retrying');\n }\n const workspaceInfo = lstatSync(context.workspace);\n if (!workspaceInfo.isDirectory() || workspaceInfo.isSymbolicLink()) throw failure('Isolation requires a real owned workspace directory');\n const workspace = realpathSync(context.workspace);\n const state = canonicalPath(config.stateDir), workspaceRoot = canonicalPath(config.workspaceRoot);\n if (workspace === workspaceRoot || contains(workspace, state) || contains(state, workspace)) throw failure('Isolation workspace must not expose control state or the whole workspace root');\n const cwd = realpathSync(context.cwd);\n const approved = [workspace, ...config.isolation.readOnlyPaths];\n if (!approved.some(path => contains(path, cwd))) throw failure('Isolated command cwd must stay in its workspace or an approved read-only root');\n const executable = executablePath(argv[0] ?? '', cwd, context.env);\n if (contains(state, executable) || contains(workspaceRoot, executable) && !contains(workspace, executable)) throw failure('An isolated command cannot execute control state or another task workspace');\n const readable = [...config.isolation.readOnlyPaths, executable, realpathSync(process.execPath)];\n for (const file of context.readableFiles ?? []) {\n if (!lstatSync(file).isFile() || lstatSync(file).isSymbolicLink()) throw failure(`Harness isolation capability requires a regular file: ${file}`);\n readable.push(realpathSync(file));\n }\n const extraWritable = (context.writableDirectories ?? []).map(path => {\n if (!lstatSync(path).isDirectory() || lstatSync(path).isSymbolicLink()) throw failure(`Harness isolation capability requires a real directory: ${path}`);\n return realpathSync(path);\n });\n const scratchParent = context.scratchParent ?? state;\n mkdirSync(scratchParent, { recursive: true, mode: 0o700 });\n const scratch = realpathSync(mkdtempSync(join(scratchParent, 'isolated-command-')));\n const cleanup = () => removeConfinedPath(scratch, scratch);\n try {\n const home = join(scratch, 'home'), temporary = join(scratch, 'tmp'), cache = join(scratch, 'cache');\n for (const path of [home, temporary, cache]) mkdirSync(path, { mode: 0o700 });\n const env: NodeJS.ProcessEnv = { ...context.env, HOME: home, TMPDIR: `${temporary}${sep}`, TMP: temporary, TEMP: temporary, XDG_CACHE_HOME: cache, GRADLE_USER_HOME: join(cache, 'gradle'), npm_config_cache: join(cache, 'npm') };\n for (const [name, fallback] of [['GOCACHE', join(cache, 'go-build')], ['GOMODCACHE', join(cache, 'go-mod')]] as const) {\n if (!env[name] || contains(state, canonicalPath(resolve(cwd, env[name]!)))) env[name] = fallback;\n }\n const systemPaths = ['/System/Library', '/usr/lib'].filter(existsSync);\n const devices = ['/dev/urandom', '/dev/random', '/dev/null'].filter(existsSync);\n const readablePaths = [...new Set([...systemPaths, ...readable])];\n const writablePaths = [...new Set([workspace, scratch, ...extraWritable])];\n const ancestors = new Set<string>(['/']);\n for (const path of [...readablePaths, ...writablePaths, ...devices, cwd]) {\n let parent = dirname(path);\n while (true) {\n ancestors.add(parent);\n const next = dirname(parent);\n if (next === parent) break;\n parent = next;\n }\n }\n const profile = [\n '(version 1)', '(deny default)', '(allow process-exec)', '(allow process-fork)', '(allow sysctl-read)',\n '(allow file-read* (literal \"/\"))',\n `(allow file-read-metadata ${[...ancestors].map(path => `(literal ${JSON.stringify(path)})`).join(' ')})`,\n `(allow file-read* ${readablePaths.map(pathRule).join(' ')} ${devices.map(path => `(literal ${JSON.stringify(path)})`).join(' ')})`,\n `(allow file-read* file-write* ${writablePaths.map(pathRule).join(' ')})`,\n '(allow file-write* (literal \"/dev/null\"))',\n ...(config.isolation.network === 'allow' ? ['(allow network*)'] : []),\n ].join('\\n');\n return { argv: ['/usr/bin/sandbox-exec', '-p', profile, executable, ...argv.slice(1)], env, cleanup };\n } catch (cause) {\n cleanup();\n throw cause;\n }\n}\n"],"mappings":"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"}
|
package/dist/macos.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { artifactOutputPath } from "./workspace.js";
|
|
2
|
+
import { closeSync, lstatSync, openSync, readSync } from "node:fs";
|
|
3
|
+
import { basename, join } from "node:path";
|
|
4
|
+
// src/macos.ts
|
|
5
|
+
/** Validate native output with system tooling, not just the file extension. */
|
|
6
|
+
async function validateMacOSArtifact(path, target, exec) {
|
|
7
|
+
if (target === "dmg") {
|
|
8
|
+
const result = await exec([
|
|
9
|
+
"/usr/bin/hdiutil",
|
|
10
|
+
"verify",
|
|
11
|
+
path
|
|
12
|
+
]);
|
|
13
|
+
if (result.code !== 0) throw new Error(`Invalid macOS disk image: ${result.stderr || result.stdout}`);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const info = artifactOutputPath(path, "Contents/Info.plist");
|
|
17
|
+
if (!lstatSync(info).isFile()) throw new Error("macOS application requires a regular Contents/Info.plist");
|
|
18
|
+
const result = await exec([
|
|
19
|
+
"/usr/bin/plutil",
|
|
20
|
+
"-extract",
|
|
21
|
+
"CFBundleExecutable",
|
|
22
|
+
"raw",
|
|
23
|
+
"-o",
|
|
24
|
+
"-",
|
|
25
|
+
info
|
|
26
|
+
]);
|
|
27
|
+
const executable = result.stdout.trim();
|
|
28
|
+
if (result.code !== 0 || !executable || executable === "." || executable === ".." || basename(executable) !== executable || executable.includes("\\")) throw new Error("macOS Info.plist must identify an in-bundle executable by file name");
|
|
29
|
+
const binary = artifactOutputPath(path, join("Contents", "MacOS", executable));
|
|
30
|
+
const stat = lstatSync(binary);
|
|
31
|
+
if (!stat.isFile() || stat.size < 32 || !(stat.mode & 73)) throw new Error("macOS application executable is missing, empty or not executable");
|
|
32
|
+
const header = Buffer.allocUnsafe(4);
|
|
33
|
+
const descriptor = openSync(binary, "r");
|
|
34
|
+
try {
|
|
35
|
+
if (readSync(descriptor, header, 0, 4, 0) !== 4 || ![
|
|
36
|
+
"feedfacf",
|
|
37
|
+
"cffaedfe",
|
|
38
|
+
"cafebabe",
|
|
39
|
+
"bebafeca",
|
|
40
|
+
"cafebabf",
|
|
41
|
+
"bfbafeca"
|
|
42
|
+
].includes(header.toString("hex"))) throw new Error("macOS application executable must be a Mach-O binary, not a script or placeholder");
|
|
43
|
+
} finally {
|
|
44
|
+
closeSync(descriptor);
|
|
45
|
+
}
|
|
46
|
+
const signed = await exec([
|
|
47
|
+
"/usr/bin/codesign",
|
|
48
|
+
"--verify",
|
|
49
|
+
"--deep",
|
|
50
|
+
"--strict",
|
|
51
|
+
path
|
|
52
|
+
]);
|
|
53
|
+
if (signed.code !== 0) throw new Error(`macOS local application signature is invalid: ${signed.stderr || signed.stdout}`);
|
|
54
|
+
}
|
|
55
|
+
export { validateMacOSArtifact };
|
|
56
|
+
|
|
57
|
+
//# sourceMappingURL=macos.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"macos.js","names":[],"sources":["../src/macos.ts"],"sourcesContent":["import { basename, join } from 'node:path';\nimport { closeSync, lstatSync, openSync, readSync } from 'node:fs';\nimport { artifactOutputPath } from './workspace.js';\nimport type { Run } from './process.js';\n\n\n/** Validate native output with system tooling, not just the file extension. */\nexport async function validateMacOSArtifact(path: string, target: 'app' | 'dmg', exec: Run): Promise<void> {\n if (target === 'dmg') {\n const result = await exec(['/usr/bin/hdiutil', 'verify', path]);\n if (result.code !== 0) throw new Error(`Invalid macOS disk image: ${result.stderr || result.stdout}`);\n return;\n }\n const info = artifactOutputPath(path, 'Contents/Info.plist');\n if (!lstatSync(info).isFile()) throw new Error('macOS application requires a regular Contents/Info.plist');\n const result = await exec(['/usr/bin/plutil', '-extract', 'CFBundleExecutable', 'raw', '-o', '-', info]);\n const executable = result.stdout.trim();\n if (result.code !== 0 || !executable || executable === '.' || executable === '..' || basename(executable) !== executable || executable.includes('\\\\')) throw new Error('macOS Info.plist must identify an in-bundle executable by file name');\n const binary = artifactOutputPath(path, join('Contents', 'MacOS', executable));\n const stat = lstatSync(binary);\n if (!stat.isFile() || stat.size < 32 || !(stat.mode & 0o111)) throw new Error('macOS application executable is missing, empty or not executable');\n const header = Buffer.allocUnsafe(4);\n const descriptor = openSync(binary, 'r');\n try {\n if (readSync(descriptor, header, 0, 4, 0) !== 4 || !['feedfacf', 'cffaedfe', 'cafebabe', 'bebafeca', 'cafebabf', 'bfbafeca'].includes(header.toString('hex'))) throw new Error('macOS application executable must be a Mach-O binary, not a script or placeholder');\n } finally { closeSync(descriptor); }\n const signed = await exec(['/usr/bin/codesign', '--verify', '--deep', '--strict', path]);\n if (signed.code !== 0) throw new Error(`macOS local application signature is invalid: ${signed.stderr || signed.stdout}`);\n}\n"],"mappings":";;;;;AAOA,eAAsB,sBAAsB,MAAc,QAAuB,MAA0B;CACzG,IAAI,WAAW,OAAO;EACpB,MAAM,SAAS,MAAM,KAAK;GAAC;GAAoB;GAAU;EAAI,CAAC;EAC9D,IAAI,OAAO,SAAS,GAAG,MAAM,IAAI,MAAM,6BAA6B,OAAO,UAAU,OAAO,QAAQ;EACpG;CACF;CACA,MAAM,OAAO,mBAAmB,MAAM,qBAAqB;CAC3D,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC,OAAO,GAAG,MAAM,IAAI,MAAM,0DAA0D;CACzG,MAAM,SAAS,MAAM,KAAK;EAAC;EAAmB;EAAY;EAAsB;EAAO;EAAM;EAAK;CAAI,CAAC;CACvG,MAAM,aAAa,OAAO,OAAO,KAAK;CACtC,IAAI,OAAO,SAAS,KAAK,CAAC,cAAc,eAAe,OAAO,eAAe,QAAQ,SAAS,UAAU,MAAM,cAAc,WAAW,SAAS,IAAI,GAAG,MAAM,IAAI,MAAM,qEAAqE;CAC5O,MAAM,SAAS,mBAAmB,MAAM,KAAK,YAAY,SAAS,UAAU,CAAC;CAC7E,MAAM,OAAO,UAAU,MAAM;CAC7B,IAAI,CAAC,KAAK,OAAO,KAAK,KAAK,OAAO,MAAM,EAAE,KAAK,OAAO,KAAQ,MAAM,IAAI,MAAM,kEAAkE;CAChJ,MAAM,SAAS,OAAO,YAAY,CAAC;CACnC,MAAM,aAAa,SAAS,QAAQ,GAAG;CACvC,IAAI;EACF,IAAI,SAAS,YAAY,QAAQ,GAAG,GAAG,CAAC,MAAM,KAAK,CAAC;GAAC;GAAY;GAAY;GAAY;GAAY;GAAY;EAAU,CAAC,CAAC,SAAS,OAAO,SAAS,KAAK,CAAC,GAAG,MAAM,IAAI,MAAM,mFAAmF;CACpQ,UAAU;EAAE,UAAU,UAAU;CAAG;CACnC,MAAM,SAAS,MAAM,KAAK;EAAC;EAAqB;EAAY;EAAU;EAAY;CAAI,CAAC;CACvF,IAAI,OAAO,SAAS,GAAG,MAAM,IAAI,MAAM,iDAAiD,OAAO,UAAU,OAAO,QAAQ;AAC1H"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { basename, posix } from "node:path";
|
|
2
|
+
// src/mygo-build.ts
|
|
3
|
+
/** Accept a direct, local MyGo build, never a wrapper that can add publishing flags. */
|
|
4
|
+
function validateMyGoPackaging(config, screenshotsDir) {
|
|
5
|
+
const profile = config.packaging;
|
|
6
|
+
if (!profile || profile.provider !== "local" || profile.commands.length !== 1) throw Object.assign(/* @__PURE__ */ new Error("MyGo requires one direct local build command"), { code: "SETUP_REQUIRED" });
|
|
7
|
+
const argv = profile.commands[0];
|
|
8
|
+
let start;
|
|
9
|
+
if (basename(argv[0]) === "mygo" && argv[1] === "build") start = 2;
|
|
10
|
+
else if (basename(argv[0]) === "go" && argv[1] === "tool" && argv[2] === "mygo" && argv[3] === "build") start = 4;
|
|
11
|
+
else if (basename(argv[0]) === "go" && argv[1] === "run" && /^github\.com\/egoist\/mygo\/cmd\/mygo@v\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(argv[2] ?? "") && argv[3] === "build") start = 4;
|
|
12
|
+
else throw Object.assign(/* @__PURE__ */ new Error("MyGo packaging must use a pinned go tool mygo build, installed mygo build, or version-pinned go run command"), { code: "SETUP_REQUIRED" });
|
|
13
|
+
const flags = /* @__PURE__ */ new Map();
|
|
14
|
+
for (let index = start; index < argv.length; index++) {
|
|
15
|
+
const flag = argv[index];
|
|
16
|
+
if (flags.has(flag)) throw Object.assign(/* @__PURE__ */ new Error(`Duplicate MyGo build flag: ${flag}`), { code: "SETUP_REQUIRED" });
|
|
17
|
+
if (flag === "-sign" || flag === "-platform" || flag === "-o") {
|
|
18
|
+
const value = argv[++index];
|
|
19
|
+
if (!value) throw Object.assign(/* @__PURE__ */ new Error(`Missing MyGo flag value: ${flag}`), { code: "SETUP_REQUIRED" });
|
|
20
|
+
flags.set(flag, value);
|
|
21
|
+
} else if (flag === "-skip-notarize" || flag === "-skip-dmg" || flag === "-skip-build-command") flags.set(flag, "true");
|
|
22
|
+
else throw Object.assign(/* @__PURE__ */ new Error(`MyGo local packaging does not permit ${flag}; upload, notarization and custom wrapper commands are out of scope`), { code: "SETUP_REQUIRED" });
|
|
23
|
+
}
|
|
24
|
+
if (flags.get("-sign") !== "-" || !flags.has("-skip-notarize")) throw Object.assign(/* @__PURE__ */ new Error("MyGo local builds require -sign - and -skip-notarize, even when project configuration enables signing/notarization"), { code: "SETUP_REQUIRED" });
|
|
25
|
+
const platform = flags.get("-platform");
|
|
26
|
+
if (!platform || !/^darwin\/(?:arm64|amd64|universal)$/.test(platform)) throw Object.assign(/* @__PURE__ */ new Error("MyGo packaging requires one explicit darwin/arm64, darwin/amd64 or darwin/universal target"), { code: "SETUP_REQUIRED" });
|
|
27
|
+
if (flags.get("-o") !== "build") throw Object.assign(/* @__PURE__ */ new Error("MyGo packaging requires -o build so output cannot replace project source"), { code: "SETUP_REQUIRED" });
|
|
28
|
+
const prefix = `build/${platform.replace("/", "-")}/`;
|
|
29
|
+
if (!profile.artifactPath.startsWith(prefix)) throw Object.assign(/* @__PURE__ */ new Error(`MyGo artifactPath must be inside ${prefix}`), { code: "SETUP_REQUIRED" });
|
|
30
|
+
if (screenshotsDir !== void 0) {
|
|
31
|
+
const screenshot = posix.normalize(screenshotsDir);
|
|
32
|
+
if (posix.isAbsolute(screenshot) || screenshot === ".." || screenshot.startsWith("../") || screenshot.includes("\\")) throw Object.assign(/* @__PURE__ */ new Error("MyGo screenshotsDir must stay within the selected project"), { code: "SETUP_REQUIRED" });
|
|
33
|
+
const outputDirectory = prefix.slice(0, -1);
|
|
34
|
+
if ([posix.relative(outputDirectory, screenshot), posix.relative(screenshot, outputDirectory)].some((path) => path === "" || path !== ".." && !path.startsWith("../") && !posix.isAbsolute(path))) throw Object.assign(/* @__PURE__ */ new Error("MyGo screenshotsDir must not overlap the platform output directory replaced during packaging"), { code: "SETUP_REQUIRED" });
|
|
35
|
+
}
|
|
36
|
+
if (config.packageTarget === "app" ? !flags.has("-skip-dmg") : flags.has("-skip-dmg")) throw Object.assign(/* @__PURE__ */ new Error("MyGo app builds require -skip-dmg; dmg builds must create the disk image"), { code: "SETUP_REQUIRED" });
|
|
37
|
+
}
|
|
38
|
+
/** Doctor and tracked execution share the same gate for every recognizable direct build. */
|
|
39
|
+
function validateMyGoCommand(config, argv) {
|
|
40
|
+
const tool = basename(argv[0] ?? "");
|
|
41
|
+
const buildIndex = argv.indexOf("build");
|
|
42
|
+
if (buildIndex <= 0) return;
|
|
43
|
+
if (!(tool === "mygo" || tool === "go" && argv.slice(1, buildIndex).some((value) => value === "mygo" || value === "github.com/egoist/mygo/cmd/mygo" || value.startsWith("github.com/egoist/mygo/cmd/mygo@")))) return;
|
|
44
|
+
validateMyGoPackaging({
|
|
45
|
+
...config,
|
|
46
|
+
packageTarget: argv.includes("-skip-dmg") ? "app" : "dmg",
|
|
47
|
+
packaging: config.packaging ? {
|
|
48
|
+
...config.packaging,
|
|
49
|
+
commands: [argv]
|
|
50
|
+
} : void 0
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
export { validateMyGoCommand, validateMyGoPackaging };
|
|
54
|
+
|
|
55
|
+
//# sourceMappingURL=mygo-build.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mygo-build.js","names":[],"sources":["../src/mygo-build.ts"],"sourcesContent":["import { basename, posix } from 'node:path';\nimport type { Config } from './schemas.js';\n\n/** Accept a direct, local MyGo build, never a wrapper that can add publishing flags. */\nexport function validateMyGoPackaging(config: Config, screenshotsDir?: string): void {\n const profile = config.packaging;\n if (!profile || profile.provider !== 'local' || profile.commands.length !== 1) throw Object.assign(new Error('MyGo requires one direct local build command'), { code: 'SETUP_REQUIRED' });\n const argv = profile.commands[0]!;\n let start: number;\n if (basename(argv[0]!) === 'mygo' && argv[1] === 'build') start = 2;\n else if (basename(argv[0]!) === 'go' && argv[1] === 'tool' && argv[2] === 'mygo' && argv[3] === 'build') start = 4;\n else if (basename(argv[0]!) === 'go' && argv[1] === 'run' && /^github\\.com\\/egoist\\/mygo\\/cmd\\/mygo@v\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?$/.test(argv[2] ?? '') && argv[3] === 'build') start = 4;\n else throw Object.assign(new Error('MyGo packaging must use a pinned go tool mygo build, installed mygo build, or version-pinned go run command'), { code: 'SETUP_REQUIRED' });\n const flags = new Map<string, string>();\n for (let index = start; index < argv.length; index++) {\n const flag = argv[index]!;\n if (flags.has(flag)) throw Object.assign(new Error(`Duplicate MyGo build flag: ${flag}`), { code: 'SETUP_REQUIRED' });\n if (flag === '-sign' || flag === '-platform' || flag === '-o') {\n const value = argv[++index];\n if (!value) throw Object.assign(new Error(`Missing MyGo flag value: ${flag}`), { code: 'SETUP_REQUIRED' });\n flags.set(flag, value);\n } else if (flag === '-skip-notarize' || flag === '-skip-dmg' || flag === '-skip-build-command') flags.set(flag, 'true');\n else throw Object.assign(new Error(`MyGo local packaging does not permit ${flag}; upload, notarization and custom wrapper commands are out of scope`), { code: 'SETUP_REQUIRED' });\n }\n if (flags.get('-sign') !== '-' || !flags.has('-skip-notarize')) throw Object.assign(new Error('MyGo local builds require -sign - and -skip-notarize, even when project configuration enables signing/notarization'), { code: 'SETUP_REQUIRED' });\n const platform = flags.get('-platform');\n if (!platform || !/^darwin\\/(?:arm64|amd64|universal)$/.test(platform)) throw Object.assign(new Error('MyGo packaging requires one explicit darwin/arm64, darwin/amd64 or darwin/universal target'), { code: 'SETUP_REQUIRED' });\n if (flags.get('-o') !== 'build') throw Object.assign(new Error('MyGo packaging requires -o build so output cannot replace project source'), { code: 'SETUP_REQUIRED' });\n const prefix = `build/${platform.replace('/', '-')}/`;\n if (!profile.artifactPath.startsWith(prefix)) throw Object.assign(new Error(`MyGo artifactPath must be inside ${prefix}`), { code: 'SETUP_REQUIRED' });\n if (screenshotsDir !== undefined) {\n const screenshot = posix.normalize(screenshotsDir);\n if (posix.isAbsolute(screenshot) || screenshot === '..' || screenshot.startsWith('../') || screenshot.includes('\\\\')) throw Object.assign(new Error('MyGo screenshotsDir must stay within the selected project'), { code: 'SETUP_REQUIRED' });\n const outputDirectory = prefix.slice(0, -1);\n if ([posix.relative(outputDirectory, screenshot), posix.relative(screenshot, outputDirectory)].some(path => path === '' || path !== '..' && !path.startsWith('../') && !posix.isAbsolute(path))) throw Object.assign(new Error('MyGo screenshotsDir must not overlap the platform output directory replaced during packaging'), { code: 'SETUP_REQUIRED' });\n }\n if (config.packageTarget === 'app' ? !flags.has('-skip-dmg') : flags.has('-skip-dmg')) throw Object.assign(new Error('MyGo app builds require -skip-dmg; dmg builds must create the disk image'), { code: 'SETUP_REQUIRED' });\n}\n\n/** Doctor and tracked execution share the same gate for every recognizable direct build. */\nexport function validateMyGoCommand(config: Config, argv: string[]): void {\n const tool = basename(argv[0] ?? '');\n const buildIndex = argv.indexOf('build');\n if (buildIndex <= 0) return;\n const direct = tool === 'mygo' || tool === 'go' && argv.slice(1, buildIndex).some(value => value === 'mygo' || value === 'github.com/egoist/mygo/cmd/mygo' || value.startsWith('github.com/egoist/mygo/cmd/mygo@'));\n if (!direct) return;\n validateMyGoPackaging({ ...config, packageTarget: argv.includes('-skip-dmg') ? 'app' : 'dmg', packaging: config.packaging ? { ...config.packaging, commands: [argv] } : undefined });\n}\n"],"mappings":"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"}
|
package/dist/policy.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ResearchSchema } from "./schemas.js";
|
|
2
|
+
// src/policy.ts
|
|
3
|
+
function parseResearch(text, channel) {
|
|
4
|
+
const content = text.trim().replace(/^```(?:json)?\s*\n?/, "").replace(/\n?```$/, "");
|
|
5
|
+
let value;
|
|
6
|
+
try {
|
|
7
|
+
value = JSON.parse(content);
|
|
8
|
+
} catch {
|
|
9
|
+
throw new Error("Host did not return a JSON verdict; no approval was inferred from text or an exit code");
|
|
10
|
+
}
|
|
11
|
+
return ResearchSchema.parse(value);
|
|
12
|
+
}
|
|
13
|
+
function researchPrompt(job, portfolio) {
|
|
14
|
+
const siblings = `Other same-channel products ${JSON.stringify(portfolio)}; earlier versions of this same app are its source baseline, not competing products.`;
|
|
15
|
+
return `Research only with your own host tools. Preserve the requested ${job.spec.framework} ${job.spec.platform} project and its existing architecture. ${siblings} Assess technical feasibility, dependencies, licensing, privacy and real acceptance for this local package. Return typed JSON {"viable":boolean,"distinctness":"specific honest differentiation","risks":["..."],"plan":["concrete implementation and device acceptance step"]}. Do not promise store approval and do not add unrequested features.`;
|
|
16
|
+
}
|
|
17
|
+
function reviewInstructions(job) {
|
|
18
|
+
return `Use an independent reviewer with fresh context when supported; otherwise disclose the limitation. Review actual source and acceptance read-only. Verified source: ${job.verifiedDigest}; dependency resolution: ${job.verifiedDependencyDigest}; actual checks: ${JSON.stringify(job.config.verification)}. Reject incomplete requirements, data loss, fake screenshots, accessibility failures, placeholders, weak tests and hidden features. Return typed JSON {"approved":boolean,"findings":["blocking issue"],"acceptanceEvidence":[{"requirement":"exact acceptance string","evidence":"file/symbol + observed command or device scenario"}]}. Cover every acceptance requirement. Reviewer identity is advisory; the Harness gates typed results and unchanged execution evidence.`;
|
|
19
|
+
}
|
|
20
|
+
export { parseResearch, researchPrompt, reviewInstructions };
|
|
21
|
+
|
|
22
|
+
//# sourceMappingURL=policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.js","names":[],"sources":["../src/policy.ts"],"sourcesContent":["import { ResearchSchema } from './schemas.js';\nimport type { Job, Spec } from './schemas.js';\n\nexport function parseResearch(text: string, channel: Spec['channel']) {\n const content = text.trim().replace(/^```(?:json)?\\s*\\n?/, '').replace(/\\n?```$/, '');\n let value: unknown;\n try { value = JSON.parse(content); }\n catch { throw new Error('Host did not return a JSON verdict; no approval was inferred from text or an exit code'); }\n return ResearchSchema.parse(value);\n}\n\nexport function researchPrompt(job: Job, portfolio: Array<Pick<Spec, 'id' | 'purpose' | 'audience' | 'differentiators'>>): string {\n const siblings = `Other same-channel products ${JSON.stringify(portfolio)}; earlier versions of this same app are its source baseline, not competing products.`;\n return `Research only with your own host tools. Preserve the requested ${job.spec.framework} ${job.spec.platform} project and its existing architecture. ${siblings} Assess technical feasibility, dependencies, licensing, privacy and real acceptance for this local package. Return typed JSON {\"viable\":boolean,\"distinctness\":\"specific honest differentiation\",\"risks\":[\"...\"],\"plan\":[\"concrete implementation and device acceptance step\"]}. Do not promise store approval and do not add unrequested features.`;\n}\n\nexport function reviewInstructions(job: Job): string {\n return `Use an independent reviewer with fresh context when supported; otherwise disclose the limitation. Review actual source and acceptance read-only. Verified source: ${job.verifiedDigest}; dependency resolution: ${job.verifiedDependencyDigest}; actual checks: ${JSON.stringify(job.config.verification)}. Reject incomplete requirements, data loss, fake screenshots, accessibility failures, placeholders, weak tests and hidden features. Return typed JSON {\"approved\":boolean,\"findings\":[\"blocking issue\"],\"acceptanceEvidence\":[{\"requirement\":\"exact acceptance string\",\"evidence\":\"file/symbol + observed command or device scenario\"}]}. Cover every acceptance requirement. Reviewer identity is advisory; the Harness gates typed results and unchanged execution evidence.`;\n}\n"],"mappings":";;AAGA,SAAgB,cAAc,MAAc,SAA0B;CACpE,MAAM,UAAU,KAAK,KAAK,CAAC,CAAC,QAAQ,uBAAuB,EAAE,CAAC,CAAC,QAAQ,WAAW,EAAE;CACpF,IAAI;CACJ,IAAI;EAAE,QAAQ,KAAK,MAAM,OAAO;CAAG,QAC7B;EAAE,MAAM,IAAI,MAAM,wFAAwF;CAAG;CACnH,OAAO,eAAe,MAAM,KAAK;AACnC;AAEA,SAAgB,eAAe,KAAU,WAAyF;CAChI,MAAM,WAAW,+BAA+B,KAAK,UAAU,SAAS,EAAE;CAC1E,OAAO,kEAAkE,IAAI,KAAK,UAAU,GAAG,IAAI,KAAK,SAAS,0CAA0C,SAAS;AACtK;AAEA,SAAgB,mBAAmB,KAAkB;CACnD,OAAO,qKAAqK,IAAI,eAAe,2BAA2B,IAAI,yBAAyB,mBAAmB,KAAK,UAAU,IAAI,OAAO,YAAY,EAAE;AACpT"}
|
package/dist/preflight.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { toError } from "./schemas.js";
|
|
2
|
-
import { run } from "./process.js";
|
|
3
2
|
import { expandArgv } from "./config.js";
|
|
3
|
+
import { run } from "./process.js";
|
|
4
4
|
// src/preflight.ts
|
|
5
5
|
function prerequisiteFailure(argv, reason, detail, cause) {
|
|
6
6
|
return Object.assign(new Error(`Prerequisite ${JSON.stringify(argv)} ${detail}. Restore the configured worker environment before retrying.`, { cause }), {
|
package/dist/process.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { toError } from "./schemas.js";
|
|
2
|
-
import { randomUUID } from "node:crypto";
|
|
3
|
-
import { dirname } from "node:path";
|
|
4
|
-
import { spawn, spawnSync } from "node:child_process";
|
|
5
2
|
import { closeSync, mkdirSync, openSync, readFileSync, writeSync } from "node:fs";
|
|
3
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
4
|
+
import { dirname } from "node:path";
|
|
5
|
+
import { randomUUID } from "node:crypto";
|
|
6
6
|
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
7
7
|
// src/process.ts
|
|
8
8
|
const COMMAND_TOKEN = "APPLE_APP_FACTORY_COMMAND_TOKEN";
|