@modootoday/pmcp 0.13.1 → 0.14.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/.agents/plugins/marketplace.json +1157 -0
- package/.claude-plugin/marketplace.json +825 -0
- package/.claude-plugin/plugin.json +9 -0
- package/.mcp.json +9 -0
- package/README.md +48 -3
- package/dist/cli.js +1352 -748
- package/dist/{config-CJ43_oBS.d.ts → config-BkSpjUG6.d.ts} +1 -0
- package/dist/{embed-BBhv3y5x.d.ts → embed-CVCEz2FH.d.ts} +1 -1
- package/dist/http.d.ts +3 -3
- package/dist/http.js +216 -112
- package/dist/index.d.ts +7 -14
- package/dist/index.js +389 -269
- package/dist/mailbox/index.d.ts +1 -1
- package/dist/mailbox/index.js +25 -9
- package/dist/{server-CwWcRjqz.d.ts → server-DAcT89iv.d.ts} +1 -1
- package/dist/stdio.d.ts +1 -1
- package/dist/stdio.js +236 -128
- package/dist/tui-worker.js +25 -9
- package/docs/catalog.json +5422 -0
- package/docs/manual-library-skills.md +74 -0
- package/docs/skills.md +46 -0
- package/gemini-extension.json +11 -0
- package/mcp.json +10 -0
- package/package.json +22 -5
- package/plugin.json +10 -0
- package/plugins/agent-base-9/.claude-plugin/plugin.json +9 -0
- package/plugins/agent-base-9/LICENSE +93 -0
- package/plugins/agent-base-9/NOTICE +51 -0
- package/plugins/agent-base-9/gemini-extension.json +1 -0
- package/plugins/agent-base-9/plugin.json +10 -0
- package/plugins/agent-base-9/skills/agent-base/SKILL.md +118 -0
- package/plugins/ajv-formats-3/.claude-plugin/plugin.json +9 -0
- package/plugins/ajv-formats-3/LICENSE +93 -0
- package/plugins/ajv-formats-3/NOTICE +51 -0
- package/plugins/ajv-formats-3/gemini-extension.json +1 -0
- package/plugins/ajv-formats-3/plugin.json +10 -0
- package/plugins/ajv-formats-3/skills/ajv-formats/SKILL.md +178 -0
- package/plugins/allure-js-commons-3/.claude-plugin/plugin.json +9 -0
- package/plugins/allure-js-commons-3/LICENSE +93 -0
- package/plugins/allure-js-commons-3/NOTICE +51 -0
- package/plugins/allure-js-commons-3/gemini-extension.json +1 -0
- package/plugins/allure-js-commons-3/plugin.json +10 -0
- package/plugins/allure-js-commons-3/skills/allure-js-commons/SKILL.md +173 -0
- package/plugins/ansi-escapes-7/.claude-plugin/plugin.json +9 -0
- package/plugins/ansi-escapes-7/LICENSE +93 -0
- package/plugins/ansi-escapes-7/NOTICE +51 -0
- package/plugins/ansi-escapes-7/gemini-extension.json +1 -0
- package/plugins/ansi-escapes-7/plugin.json +10 -0
- package/plugins/ansi-escapes-7/skills/ansi-escapes/SKILL.md +189 -0
- package/plugins/ansi-styles-7/.claude-plugin/plugin.json +9 -0
- package/plugins/ansi-styles-7/LICENSE +93 -0
- package/plugins/ansi-styles-7/NOTICE +51 -0
- package/plugins/ansi-styles-7/gemini-extension.json +1 -0
- package/plugins/ansi-styles-7/plugin.json +10 -0
- package/plugins/ansi-styles-7/skills/ansi-styles/SKILL.md +161 -0
- package/plugins/app-builder-lib-26/.claude-plugin/plugin.json +9 -0
- package/plugins/app-builder-lib-26/LICENSE +93 -0
- package/plugins/app-builder-lib-26/NOTICE +51 -0
- package/plugins/app-builder-lib-26/gemini-extension.json +1 -0
- package/plugins/app-builder-lib-26/plugin.json +10 -0
- package/plugins/app-builder-lib-26/skills/app-builder-lib/SKILL.md +84 -0
- package/plugins/autoprefixer-10/.claude-plugin/plugin.json +9 -0
- package/plugins/autoprefixer-10/LICENSE +93 -0
- package/plugins/autoprefixer-10/NOTICE +51 -0
- package/plugins/autoprefixer-10/gemini-extension.json +1 -0
- package/plugins/autoprefixer-10/plugin.json +10 -0
- package/plugins/autoprefixer-10/skills/autoprefixer/SKILL.md +141 -0
- package/plugins/babel-plugin-transform-typescript-8/.claude-plugin/plugin.json +9 -0
- package/plugins/babel-plugin-transform-typescript-8/LICENSE +93 -0
- package/plugins/babel-plugin-transform-typescript-8/NOTICE +51 -0
- package/plugins/babel-plugin-transform-typescript-8/gemini-extension.json +1 -0
- package/plugins/babel-plugin-transform-typescript-8/plugin.json +10 -0
- package/plugins/babel-plugin-transform-typescript-8/skills/babel-plugin-transform-typescript/SKILL.md +135 -0
- package/plugins/bcryptjs-2/.claude-plugin/plugin.json +9 -0
- package/plugins/bcryptjs-2/LICENSE +93 -0
- package/plugins/bcryptjs-2/NOTICE +51 -0
- package/plugins/bcryptjs-2/gemini-extension.json +1 -0
- package/plugins/bcryptjs-2/plugin.json +10 -0
- package/plugins/bcryptjs-2/skills/bcryptjs/SKILL.md +78 -0
- package/plugins/bullmq-5/.claude-plugin/plugin.json +9 -0
- package/plugins/bullmq-5/LICENSE +93 -0
- package/plugins/bullmq-5/NOTICE +51 -0
- package/plugins/bullmq-5/gemini-extension.json +1 -0
- package/plugins/bullmq-5/plugin.json +10 -0
- package/plugins/bullmq-5/skills/bullmq/SKILL.md +59 -0
- package/plugins/bun-types-1/.claude-plugin/plugin.json +9 -0
- package/plugins/bun-types-1/LICENSE +93 -0
- package/plugins/bun-types-1/NOTICE +51 -0
- package/plugins/bun-types-1/gemini-extension.json +1 -0
- package/plugins/bun-types-1/plugin.json +10 -0
- package/plugins/bun-types-1/skills/bun-types/SKILL.md +119 -0
- package/plugins/chai-6/.claude-plugin/plugin.json +9 -0
- package/plugins/chai-6/LICENSE +93 -0
- package/plugins/chai-6/NOTICE +51 -0
- package/plugins/chai-6/gemini-extension.json +1 -0
- package/plugins/chai-6/plugin.json +10 -0
- package/plugins/chai-6/skills/chai/SKILL.md +160 -0
- package/plugins/chalk-6/.claude-plugin/plugin.json +9 -0
- package/plugins/chalk-6/LICENSE +93 -0
- package/plugins/chalk-6/NOTICE +51 -0
- package/plugins/chalk-6/gemini-extension.json +1 -0
- package/plugins/chalk-6/plugin.json +10 -0
- package/plugins/chalk-6/skills/chalk/SKILL.md +167 -0
- package/plugins/cli-cursor-5/.claude-plugin/plugin.json +9 -0
- package/plugins/cli-cursor-5/LICENSE +93 -0
- package/plugins/cli-cursor-5/NOTICE +51 -0
- package/plugins/cli-cursor-5/gemini-extension.json +1 -0
- package/plugins/cli-cursor-5/plugin.json +10 -0
- package/plugins/cli-cursor-5/skills/cli-cursor/SKILL.md +117 -0
- package/plugins/cli-spinners-3/.claude-plugin/plugin.json +9 -0
- package/plugins/cli-spinners-3/LICENSE +93 -0
- package/plugins/cli-spinners-3/NOTICE +51 -0
- package/plugins/cli-spinners-3/gemini-extension.json +1 -0
- package/plugins/cli-spinners-3/plugin.json +10 -0
- package/plugins/cli-spinners-3/skills/cli-spinners/SKILL.md +101 -0
- package/plugins/clickhouse-client-1/.claude-plugin/plugin.json +9 -0
- package/plugins/clickhouse-client-1/LICENSE +93 -0
- package/plugins/clickhouse-client-1/NOTICE +51 -0
- package/plugins/clickhouse-client-1/gemini-extension.json +1 -0
- package/plugins/clickhouse-client-1/plugin.json +10 -0
- package/plugins/clickhouse-client-1/skills/clickhouse-client/SKILL.md +54 -0
- package/plugins/clickhouse-client-web-1/.claude-plugin/plugin.json +9 -0
- package/plugins/clickhouse-client-web-1/LICENSE +93 -0
- package/plugins/clickhouse-client-web-1/NOTICE +51 -0
- package/plugins/clickhouse-client-web-1/gemini-extension.json +1 -0
- package/plugins/clickhouse-client-web-1/plugin.json +10 -0
- package/plugins/clickhouse-client-web-1/skills/clickhouse-client-web/SKILL.md +52 -0
- package/plugins/cliui-9/.claude-plugin/plugin.json +9 -0
- package/plugins/cliui-9/LICENSE +93 -0
- package/plugins/cliui-9/NOTICE +51 -0
- package/plugins/cliui-9/gemini-extension.json +1 -0
- package/plugins/cliui-9/plugin.json +10 -0
- package/plugins/cliui-9/skills/cliui/SKILL.md +163 -0
- package/plugins/cloudflare-workers-types-4/.claude-plugin/plugin.json +9 -0
- package/plugins/cloudflare-workers-types-4/LICENSE +93 -0
- package/plugins/cloudflare-workers-types-4/NOTICE +51 -0
- package/plugins/cloudflare-workers-types-4/gemini-extension.json +1 -0
- package/plugins/cloudflare-workers-types-4/plugin.json +10 -0
- package/plugins/cloudflare-workers-types-4/skills/cloudflare-workers-types/SKILL.md +63 -0
- package/plugins/commander-15/.claude-plugin/plugin.json +9 -0
- package/plugins/commander-15/LICENSE +93 -0
- package/plugins/commander-15/NOTICE +51 -0
- package/plugins/commander-15/gemini-extension.json +1 -0
- package/plugins/commander-15/plugin.json +10 -0
- package/plugins/commander-15/skills/commander/SKILL.md +204 -0
- package/plugins/content-type-3/.claude-plugin/plugin.json +9 -0
- package/plugins/content-type-3/LICENSE +93 -0
- package/plugins/content-type-3/NOTICE +51 -0
- package/plugins/content-type-3/gemini-extension.json +1 -0
- package/plugins/content-type-3/plugin.json +10 -0
- package/plugins/content-type-3/skills/content-type/SKILL.md +126 -0
- package/plugins/croner-10/.claude-plugin/plugin.json +9 -0
- package/plugins/croner-10/LICENSE +93 -0
- package/plugins/croner-10/NOTICE +51 -0
- package/plugins/croner-10/gemini-extension.json +1 -0
- package/plugins/croner-10/plugin.json +10 -0
- package/plugins/croner-10/skills/croner/SKILL.md +182 -0
- package/plugins/css-tree-3/.claude-plugin/plugin.json +9 -0
- package/plugins/css-tree-3/LICENSE +93 -0
- package/plugins/css-tree-3/NOTICE +51 -0
- package/plugins/css-tree-3/gemini-extension.json +1 -0
- package/plugins/css-tree-3/plugin.json +10 -0
- package/plugins/css-tree-3/skills/css-tree/SKILL.md +165 -0
- package/plugins/data-urls-7/.claude-plugin/plugin.json +9 -0
- package/plugins/data-urls-7/LICENSE +93 -0
- package/plugins/data-urls-7/NOTICE +51 -0
- package/plugins/data-urls-7/gemini-extension.json +1 -0
- package/plugins/data-urls-7/plugin.json +10 -0
- package/plugins/data-urls-7/skills/data-urls/SKILL.md +136 -0
- package/plugins/deep-eql-5/.claude-plugin/plugin.json +9 -0
- package/plugins/deep-eql-5/LICENSE +93 -0
- package/plugins/deep-eql-5/NOTICE +51 -0
- package/plugins/deep-eql-5/gemini-extension.json +1 -0
- package/plugins/deep-eql-5/plugin.json +10 -0
- package/plugins/deep-eql-5/skills/deep-eql/SKILL.md +130 -0
- package/plugins/dexie-4/.claude-plugin/plugin.json +9 -0
- package/plugins/dexie-4/LICENSE +93 -0
- package/plugins/dexie-4/NOTICE +51 -0
- package/plugins/dexie-4/gemini-extension.json +1 -0
- package/plugins/dexie-4/plugin.json +10 -0
- package/plugins/dexie-4/skills/dexie/SKILL.md +192 -0
- package/plugins/emittery-2/.claude-plugin/plugin.json +9 -0
- package/plugins/emittery-2/LICENSE +93 -0
- package/plugins/emittery-2/NOTICE +51 -0
- package/plugins/emittery-2/gemini-extension.json +1 -0
- package/plugins/emittery-2/plugin.json +10 -0
- package/plugins/emittery-2/skills/emittery/SKILL.md +234 -0
- package/plugins/eslint-10/.claude-plugin/plugin.json +9 -0
- package/plugins/eslint-10/LICENSE +93 -0
- package/plugins/eslint-10/NOTICE +51 -0
- package/plugins/eslint-10/gemini-extension.json +1 -0
- package/plugins/eslint-10/plugin.json +10 -0
- package/plugins/eslint-10/skills/eslint/SKILL.md +77 -0
- package/plugins/eslint-9/.claude-plugin/plugin.json +9 -0
- package/plugins/eslint-9/LICENSE +93 -0
- package/plugins/eslint-9/NOTICE +51 -0
- package/plugins/eslint-9/gemini-extension.json +1 -0
- package/plugins/eslint-9/plugin.json +10 -0
- package/plugins/eslint-9/skills/eslint/SKILL.md +93 -0
- package/plugins/eslint-plugin-react-hooks-7/.claude-plugin/plugin.json +9 -0
- package/plugins/eslint-plugin-react-hooks-7/LICENSE +93 -0
- package/plugins/eslint-plugin-react-hooks-7/NOTICE +51 -0
- package/plugins/eslint-plugin-react-hooks-7/gemini-extension.json +1 -0
- package/plugins/eslint-plugin-react-hooks-7/plugin.json +10 -0
- package/plugins/eslint-plugin-react-hooks-7/skills/eslint-plugin-react-hooks/SKILL.md +173 -0
- package/plugins/eslint-plugin-testing-library-7/.claude-plugin/plugin.json +9 -0
- package/plugins/eslint-plugin-testing-library-7/LICENSE +93 -0
- package/plugins/eslint-plugin-testing-library-7/NOTICE +51 -0
- package/plugins/eslint-plugin-testing-library-7/gemini-extension.json +1 -0
- package/plugins/eslint-plugin-testing-library-7/plugin.json +10 -0
- package/plugins/eslint-plugin-testing-library-7/skills/eslint-plugin-testing-library/SKILL.md +193 -0
- package/plugins/expect-type-1/.claude-plugin/plugin.json +9 -0
- package/plugins/expect-type-1/LICENSE +93 -0
- package/plugins/expect-type-1/NOTICE +51 -0
- package/plugins/expect-type-1/gemini-extension.json +1 -0
- package/plugins/expect-type-1/plugin.json +10 -0
- package/plugins/expect-type-1/skills/expect-type/SKILL.md +165 -0
- package/plugins/express-5/.claude-plugin/plugin.json +9 -0
- package/plugins/express-5/LICENSE +93 -0
- package/plugins/express-5/NOTICE +51 -0
- package/plugins/express-5/gemini-extension.json +1 -0
- package/plugins/express-5/plugin.json +10 -0
- package/plugins/express-5/skills/express/SKILL.md +179 -0
- package/plugins/fake-indexeddb-6/.claude-plugin/plugin.json +9 -0
- package/plugins/fake-indexeddb-6/LICENSE +93 -0
- package/plugins/fake-indexeddb-6/NOTICE +51 -0
- package/plugins/fake-indexeddb-6/gemini-extension.json +1 -0
- package/plugins/fake-indexeddb-6/plugin.json +10 -0
- package/plugins/fake-indexeddb-6/skills/fake-indexeddb/SKILL.md +147 -0
- package/plugins/fast-check-4/.claude-plugin/plugin.json +9 -0
- package/plugins/fast-check-4/LICENSE +93 -0
- package/plugins/fast-check-4/NOTICE +51 -0
- package/plugins/fast-check-4/gemini-extension.json +1 -0
- package/plugins/fast-check-4/plugin.json +10 -0
- package/plugins/fast-check-4/skills/fast-check/SKILL.md +168 -0
- package/plugins/figures-6/.claude-plugin/plugin.json +9 -0
- package/plugins/figures-6/LICENSE +93 -0
- package/plugins/figures-6/NOTICE +51 -0
- package/plugins/figures-6/gemini-extension.json +1 -0
- package/plugins/figures-6/plugin.json +10 -0
- package/plugins/figures-6/skills/figures/SKILL.md +99 -0
- package/plugins/firebase-12/.claude-plugin/plugin.json +9 -0
- package/plugins/firebase-12/LICENSE +93 -0
- package/plugins/firebase-12/NOTICE +51 -0
- package/plugins/firebase-12/gemini-extension.json +1 -0
- package/plugins/firebase-12/plugin.json +10 -0
- package/plugins/firebase-12/skills/firebase/SKILL.md +161 -0
- package/plugins/firebase-admin-14/.claude-plugin/plugin.json +9 -0
- package/plugins/firebase-admin-14/LICENSE +93 -0
- package/plugins/firebase-admin-14/NOTICE +51 -0
- package/plugins/firebase-admin-14/gemini-extension.json +1 -0
- package/plugins/firebase-admin-14/plugin.json +10 -0
- package/plugins/firebase-admin-14/skills/firebase-admin/SKILL.md +155 -0
- package/plugins/get-uri-8/.claude-plugin/plugin.json +9 -0
- package/plugins/get-uri-8/LICENSE +93 -0
- package/plugins/get-uri-8/NOTICE +51 -0
- package/plugins/get-uri-8/gemini-extension.json +1 -0
- package/plugins/get-uri-8/plugin.json +10 -0
- package/plugins/get-uri-8/skills/get-uri/SKILL.md +112 -0
- package/plugins/google-uuid-1/.claude-plugin/plugin.json +9 -0
- package/plugins/google-uuid-1/LICENSE +93 -0
- package/plugins/google-uuid-1/NOTICE +51 -0
- package/plugins/google-uuid-1/gemini-extension.json +1 -0
- package/plugins/google-uuid-1/plugin.json +10 -0
- package/plugins/google-uuid-1/skills/google-uuid/SKILL.md +70 -0
- package/plugins/graphql-16/.claude-plugin/plugin.json +9 -0
- package/plugins/graphql-16/LICENSE +93 -0
- package/plugins/graphql-16/NOTICE +51 -0
- package/plugins/graphql-16/gemini-extension.json +1 -0
- package/plugins/graphql-16/plugin.json +10 -0
- package/plugins/graphql-16/skills/graphql/SKILL.md +56 -0
- package/plugins/graphql-17/.claude-plugin/plugin.json +9 -0
- package/plugins/graphql-17/LICENSE +93 -0
- package/plugins/graphql-17/NOTICE +51 -0
- package/plugins/graphql-17/gemini-extension.json +1 -0
- package/plugins/graphql-17/plugin.json +10 -0
- package/plugins/graphql-17/skills/graphql/SKILL.md +159 -0
- package/plugins/graphql-yoga-5/.claude-plugin/plugin.json +9 -0
- package/plugins/graphql-yoga-5/LICENSE +93 -0
- package/plugins/graphql-yoga-5/NOTICE +51 -0
- package/plugins/graphql-yoga-5/gemini-extension.json +1 -0
- package/plugins/graphql-yoga-5/plugin.json +10 -0
- package/plugins/graphql-yoga-5/skills/graphql-yoga/SKILL.md +68 -0
- package/plugins/happy-dom-20/.claude-plugin/plugin.json +9 -0
- package/plugins/happy-dom-20/LICENSE +93 -0
- package/plugins/happy-dom-20/NOTICE +51 -0
- package/plugins/happy-dom-20/gemini-extension.json +1 -0
- package/plugins/happy-dom-20/plugin.json +10 -0
- package/plugins/happy-dom-20/skills/happy-dom/SKILL.md +172 -0
- package/plugins/hono-4/.claude-plugin/plugin.json +9 -0
- package/plugins/hono-4/LICENSE +93 -0
- package/plugins/hono-4/NOTICE +51 -0
- package/plugins/hono-4/gemini-extension.json +1 -0
- package/plugins/hono-4/plugin.json +10 -0
- package/plugins/hono-4/skills/hono/SKILL.md +203 -0
- package/plugins/http-proxy-agent-9/.claude-plugin/plugin.json +9 -0
- package/plugins/http-proxy-agent-9/LICENSE +93 -0
- package/plugins/http-proxy-agent-9/NOTICE +51 -0
- package/plugins/http-proxy-agent-9/gemini-extension.json +1 -0
- package/plugins/http-proxy-agent-9/plugin.json +10 -0
- package/plugins/http-proxy-agent-9/skills/http-proxy-agent/SKILL.md +142 -0
- package/plugins/human-signals-8/.claude-plugin/plugin.json +9 -0
- package/plugins/human-signals-8/LICENSE +93 -0
- package/plugins/human-signals-8/NOTICE +51 -0
- package/plugins/human-signals-8/gemini-extension.json +1 -0
- package/plugins/human-signals-8/plugin.json +10 -0
- package/plugins/human-signals-8/skills/human-signals/SKILL.md +81 -0
- package/plugins/inquirer-14/.claude-plugin/plugin.json +9 -0
- package/plugins/inquirer-14/LICENSE +93 -0
- package/plugins/inquirer-14/NOTICE +51 -0
- package/plugins/inquirer-14/gemini-extension.json +1 -0
- package/plugins/inquirer-14/plugin.json +10 -0
- package/plugins/inquirer-14/skills/inquirer/SKILL.md +152 -0
- package/plugins/ioredis-5/.claude-plugin/plugin.json +9 -0
- package/plugins/ioredis-5/LICENSE +93 -0
- package/plugins/ioredis-5/NOTICE +51 -0
- package/plugins/ioredis-5/gemini-extension.json +1 -0
- package/plugins/ioredis-5/plugin.json +10 -0
- package/plugins/ioredis-5/skills/ioredis/SKILL.md +63 -0
- package/plugins/ioredis-6/.claude-plugin/plugin.json +9 -0
- package/plugins/ioredis-6/LICENSE +93 -0
- package/plugins/ioredis-6/NOTICE +51 -0
- package/plugins/ioredis-6/gemini-extension.json +1 -0
- package/plugins/ioredis-6/plugin.json +10 -0
- package/plugins/ioredis-6/skills/ioredis/SKILL.md +204 -0
- package/plugins/ip-address-10/.claude-plugin/plugin.json +9 -0
- package/plugins/ip-address-10/LICENSE +93 -0
- package/plugins/ip-address-10/NOTICE +51 -0
- package/plugins/ip-address-10/gemini-extension.json +1 -0
- package/plugins/ip-address-10/plugin.json +10 -0
- package/plugins/ip-address-10/skills/ip-address/SKILL.md +184 -0
- package/plugins/jackspeak-4/.claude-plugin/plugin.json +9 -0
- package/plugins/jackspeak-4/LICENSE +93 -0
- package/plugins/jackspeak-4/NOTICE +51 -0
- package/plugins/jackspeak-4/gemini-extension.json +1 -0
- package/plugins/jackspeak-4/plugin.json +10 -0
- package/plugins/jackspeak-4/skills/jackspeak/SKILL.md +162 -0
- package/plugins/jake-12/.claude-plugin/plugin.json +9 -0
- package/plugins/jake-12/LICENSE +93 -0
- package/plugins/jake-12/NOTICE +51 -0
- package/plugins/jake-12/gemini-extension.json +1 -0
- package/plugins/jake-12/plugin.json +10 -0
- package/plugins/jake-12/skills/jake/SKILL.md +182 -0
- package/plugins/jasmine-core-7/.claude-plugin/plugin.json +9 -0
- package/plugins/jasmine-core-7/LICENSE +93 -0
- package/plugins/jasmine-core-7/NOTICE +51 -0
- package/plugins/jasmine-core-7/gemini-extension.json +1 -0
- package/plugins/jasmine-core-7/plugin.json +10 -0
- package/plugins/jasmine-core-7/skills/jasmine-core/SKILL.md +173 -0
- package/plugins/jest-cli-30/.claude-plugin/plugin.json +9 -0
- package/plugins/jest-cli-30/LICENSE +93 -0
- package/plugins/jest-cli-30/NOTICE +51 -0
- package/plugins/jest-cli-30/gemini-extension.json +1 -0
- package/plugins/jest-cli-30/plugin.json +10 -0
- package/plugins/jest-cli-30/skills/jest-cli/SKILL.md +121 -0
- package/plugins/jest-extended-7/.claude-plugin/plugin.json +9 -0
- package/plugins/jest-extended-7/LICENSE +93 -0
- package/plugins/jest-extended-7/NOTICE +51 -0
- package/plugins/jest-extended-7/gemini-extension.json +1 -0
- package/plugins/jest-extended-7/plugin.json +10 -0
- package/plugins/jest-extended-7/skills/jest-extended/SKILL.md +125 -0
- package/plugins/jsdom-30/.claude-plugin/plugin.json +9 -0
- package/plugins/jsdom-30/LICENSE +93 -0
- package/plugins/jsdom-30/NOTICE +51 -0
- package/plugins/jsdom-30/gemini-extension.json +1 -0
- package/plugins/jsdom-30/plugin.json +10 -0
- package/plugins/jsdom-30/skills/jsdom/SKILL.md +64 -0
- package/plugins/level-supports-6/.claude-plugin/plugin.json +9 -0
- package/plugins/level-supports-6/LICENSE +93 -0
- package/plugins/level-supports-6/NOTICE +51 -0
- package/plugins/level-supports-6/gemini-extension.json +1 -0
- package/plugins/level-supports-6/plugin.json +10 -0
- package/plugins/level-supports-6/skills/level-supports/SKILL.md +178 -0
- package/plugins/lowdb-7/.claude-plugin/plugin.json +9 -0
- package/plugins/lowdb-7/LICENSE +93 -0
- package/plugins/lowdb-7/NOTICE +51 -0
- package/plugins/lowdb-7/gemini-extension.json +1 -0
- package/plugins/lowdb-7/plugin.json +10 -0
- package/plugins/lowdb-7/skills/lowdb/SKILL.md +163 -0
- package/plugins/make-fetch-happen-16/.claude-plugin/plugin.json +9 -0
- package/plugins/make-fetch-happen-16/LICENSE +93 -0
- package/plugins/make-fetch-happen-16/NOTICE +51 -0
- package/plugins/make-fetch-happen-16/gemini-extension.json +1 -0
- package/plugins/make-fetch-happen-16/plugin.json +10 -0
- package/plugins/make-fetch-happen-16/skills/make-fetch-happen/SKILL.md +171 -0
- package/plugins/mdx-bundler-10/.claude-plugin/plugin.json +9 -0
- package/plugins/mdx-bundler-10/LICENSE +93 -0
- package/plugins/mdx-bundler-10/NOTICE +51 -0
- package/plugins/mdx-bundler-10/gemini-extension.json +1 -0
- package/plugins/mdx-bundler-10/plugin.json +10 -0
- package/plugins/mdx-bundler-10/skills/mdx-bundler/SKILL.md +133 -0
- package/plugins/mocha-12/.claude-plugin/plugin.json +9 -0
- package/plugins/mocha-12/LICENSE +93 -0
- package/plugins/mocha-12/NOTICE +51 -0
- package/plugins/mocha-12/gemini-extension.json +1 -0
- package/plugins/mocha-12/plugin.json +10 -0
- package/plugins/mocha-12/skills/mocha/SKILL.md +157 -0
- package/plugins/modelcontextprotocol-client-2/.claude-plugin/plugin.json +9 -0
- package/plugins/modelcontextprotocol-client-2/LICENSE +93 -0
- package/plugins/modelcontextprotocol-client-2/NOTICE +51 -0
- package/plugins/modelcontextprotocol-client-2/gemini-extension.json +1 -0
- package/plugins/modelcontextprotocol-client-2/plugin.json +10 -0
- package/plugins/modelcontextprotocol-client-2/skills/modelcontextprotocol-client/SKILL.md +86 -0
- package/plugins/modelcontextprotocol-sdk-1/.claude-plugin/plugin.json +9 -0
- package/plugins/modelcontextprotocol-sdk-1/LICENSE +93 -0
- package/plugins/modelcontextprotocol-sdk-1/NOTICE +51 -0
- package/plugins/modelcontextprotocol-sdk-1/gemini-extension.json +1 -0
- package/plugins/modelcontextprotocol-sdk-1/plugin.json +10 -0
- package/plugins/modelcontextprotocol-sdk-1/skills/modelcontextprotocol-sdk/SKILL.md +80 -0
- package/plugins/modelcontextprotocol-server-2/.claude-plugin/plugin.json +9 -0
- package/plugins/modelcontextprotocol-server-2/LICENSE +93 -0
- package/plugins/modelcontextprotocol-server-2/NOTICE +51 -0
- package/plugins/modelcontextprotocol-server-2/gemini-extension.json +1 -0
- package/plugins/modelcontextprotocol-server-2/plugin.json +10 -0
- package/plugins/modelcontextprotocol-server-2/skills/modelcontextprotocol-server/SKILL.md +80 -0
- package/plugins/msw-2/.claude-plugin/plugin.json +9 -0
- package/plugins/msw-2/LICENSE +93 -0
- package/plugins/msw-2/NOTICE +51 -0
- package/plugins/msw-2/gemini-extension.json +1 -0
- package/plugins/msw-2/plugin.json +10 -0
- package/plugins/msw-2/skills/msw/SKILL.md +182 -0
- package/plugins/negotiator-1/.claude-plugin/plugin.json +9 -0
- package/plugins/negotiator-1/LICENSE +93 -0
- package/plugins/negotiator-1/NOTICE +51 -0
- package/plugins/negotiator-1/gemini-extension.json +1 -0
- package/plugins/negotiator-1/plugin.json +10 -0
- package/plugins/negotiator-1/skills/negotiator/SKILL.md +180 -0
- package/plugins/npm-run-path-6/.claude-plugin/plugin.json +9 -0
- package/plugins/npm-run-path-6/LICENSE +93 -0
- package/plugins/npm-run-path-6/NOTICE +51 -0
- package/plugins/npm-run-path-6/gemini-extension.json +1 -0
- package/plugins/npm-run-path-6/plugin.json +10 -0
- package/plugins/npm-run-path-6/skills/npm-run-path/SKILL.md +137 -0
- package/plugins/nyc-18/.claude-plugin/plugin.json +9 -0
- package/plugins/nyc-18/LICENSE +93 -0
- package/plugins/nyc-18/NOTICE +51 -0
- package/plugins/nyc-18/gemini-extension.json +1 -0
- package/plugins/nyc-18/plugin.json +10 -0
- package/plugins/nyc-18/skills/nyc/SKILL.md +152 -0
- package/plugins/ow-3/.claude-plugin/plugin.json +9 -0
- package/plugins/ow-3/LICENSE +93 -0
- package/plugins/ow-3/NOTICE +51 -0
- package/plugins/ow-3/gemini-extension.json +1 -0
- package/plugins/ow-3/plugin.json +10 -0
- package/plugins/ow-3/skills/ow/SKILL.md +159 -0
- package/plugins/pac-proxy-agent-9/.claude-plugin/plugin.json +9 -0
- package/plugins/pac-proxy-agent-9/LICENSE +93 -0
- package/plugins/pac-proxy-agent-9/NOTICE +51 -0
- package/plugins/pac-proxy-agent-9/gemini-extension.json +1 -0
- package/plugins/pac-proxy-agent-9/plugin.json +10 -0
- package/plugins/pac-proxy-agent-9/skills/pac-proxy-agent/SKILL.md +111 -0
- package/plugins/pgsql-ast-parser-12/.claude-plugin/plugin.json +9 -0
- package/plugins/pgsql-ast-parser-12/LICENSE +93 -0
- package/plugins/pgsql-ast-parser-12/NOTICE +51 -0
- package/plugins/pgsql-ast-parser-12/gemini-extension.json +1 -0
- package/plugins/pgsql-ast-parser-12/plugin.json +10 -0
- package/plugins/pgsql-ast-parser-12/skills/pgsql-ast-parser/SKILL.md +136 -0
- package/plugins/playwright-test-1/.claude-plugin/plugin.json +9 -0
- package/plugins/playwright-test-1/LICENSE +93 -0
- package/plugins/playwright-test-1/NOTICE +51 -0
- package/plugins/playwright-test-1/gemini-extension.json +1 -0
- package/plugins/playwright-test-1/plugin.json +10 -0
- package/plugins/playwright-test-1/skills/playwright-test/SKILL.md +222 -0
- package/plugins/pmcp/.claude-plugin/plugin.json +9 -0
- package/plugins/pmcp/.mcp.json +9 -0
- package/plugins/pmcp/LICENSE +93 -0
- package/plugins/pmcp/NOTICE +51 -0
- package/plugins/pmcp/gemini-extension.json +11 -0
- package/plugins/pmcp/mcp.json +10 -0
- package/plugins/pmcp/plugin.json +10 -0
- package/plugins/pmcp/skills/package-skill-catalog/SKILL.md +54 -0
- package/plugins/pothos-core-4/.claude-plugin/plugin.json +9 -0
- package/plugins/pothos-core-4/LICENSE +93 -0
- package/plugins/pothos-core-4/NOTICE +51 -0
- package/plugins/pothos-core-4/gemini-extension.json +1 -0
- package/plugins/pothos-core-4/plugin.json +10 -0
- package/plugins/pothos-core-4/skills/pothos-core/SKILL.md +68 -0
- package/plugins/prettier-3/.claude-plugin/plugin.json +9 -0
- package/plugins/prettier-3/LICENSE +93 -0
- package/plugins/prettier-3/NOTICE +51 -0
- package/plugins/prettier-3/gemini-extension.json +1 -0
- package/plugins/prettier-3/plugin.json +10 -0
- package/plugins/prettier-3/skills/prettier/SKILL.md +69 -0
- package/plugins/protobufjs-8/.claude-plugin/plugin.json +9 -0
- package/plugins/protobufjs-8/LICENSE +93 -0
- package/plugins/protobufjs-8/NOTICE +51 -0
- package/plugins/protobufjs-8/gemini-extension.json +1 -0
- package/plugins/protobufjs-8/plugin.json +10 -0
- package/plugins/protobufjs-8/skills/protobufjs/SKILL.md +128 -0
- package/plugins/proxy-agent-8/.claude-plugin/plugin.json +9 -0
- package/plugins/proxy-agent-8/LICENSE +93 -0
- package/plugins/proxy-agent-8/NOTICE +51 -0
- package/plugins/proxy-agent-8/gemini-extension.json +1 -0
- package/plugins/proxy-agent-8/plugin.json +10 -0
- package/plugins/proxy-agent-8/skills/proxy-agent/SKILL.md +120 -0
- package/plugins/pydantic-2/.claude-plugin/plugin.json +9 -0
- package/plugins/pydantic-2/LICENSE +93 -0
- package/plugins/pydantic-2/NOTICE +51 -0
- package/plugins/pydantic-2/gemini-extension.json +1 -0
- package/plugins/pydantic-2/plugin.json +10 -0
- package/plugins/pydantic-2/skills/pydantic/SKILL.md +74 -0
- package/plugins/react-19/.claude-plugin/plugin.json +9 -0
- package/plugins/react-19/LICENSE +93 -0
- package/plugins/react-19/NOTICE +51 -0
- package/plugins/react-19/gemini-extension.json +1 -0
- package/plugins/react-19/plugin.json +10 -0
- package/plugins/react-19/skills/react/SKILL.md +150 -0
- package/plugins/react-dom-19/.claude-plugin/plugin.json +9 -0
- package/plugins/react-dom-19/LICENSE +93 -0
- package/plugins/react-dom-19/NOTICE +51 -0
- package/plugins/react-dom-19/gemini-extension.json +1 -0
- package/plugins/react-dom-19/plugin.json +10 -0
- package/plugins/react-dom-19/skills/react-dom/SKILL.md +178 -0
- package/plugins/react-icons-5/.claude-plugin/plugin.json +9 -0
- package/plugins/react-icons-5/LICENSE +93 -0
- package/plugins/react-icons-5/NOTICE +51 -0
- package/plugins/react-icons-5/gemini-extension.json +1 -0
- package/plugins/react-icons-5/plugin.json +10 -0
- package/plugins/react-icons-5/skills/react-icons/SKILL.md +194 -0
- package/plugins/react-is-19/.claude-plugin/plugin.json +9 -0
- package/plugins/react-is-19/LICENSE +93 -0
- package/plugins/react-is-19/NOTICE +51 -0
- package/plugins/react-is-19/gemini-extension.json +1 -0
- package/plugins/react-is-19/plugin.json +10 -0
- package/plugins/react-is-19/skills/react-is/SKILL.md +131 -0
- package/plugins/react-markdown-10/.claude-plugin/plugin.json +9 -0
- package/plugins/react-markdown-10/LICENSE +93 -0
- package/plugins/react-markdown-10/NOTICE +51 -0
- package/plugins/react-markdown-10/gemini-extension.json +1 -0
- package/plugins/react-markdown-10/plugin.json +10 -0
- package/plugins/react-markdown-10/skills/react-markdown/SKILL.md +163 -0
- package/plugins/react-router-8/.claude-plugin/plugin.json +9 -0
- package/plugins/react-router-8/LICENSE +93 -0
- package/plugins/react-router-8/NOTICE +51 -0
- package/plugins/react-router-8/gemini-extension.json +1 -0
- package/plugins/react-router-8/plugin.json +10 -0
- package/plugins/react-router-8/skills/react-router/SKILL.md +115 -0
- package/plugins/release-it-21/.claude-plugin/plugin.json +9 -0
- package/plugins/release-it-21/LICENSE +93 -0
- package/plugins/release-it-21/NOTICE +51 -0
- package/plugins/release-it-21/gemini-extension.json +1 -0
- package/plugins/release-it-21/plugin.json +10 -0
- package/plugins/release-it-21/skills/release-it/SKILL.md +202 -0
- package/plugins/reselect-5/.claude-plugin/plugin.json +9 -0
- package/plugins/reselect-5/LICENSE +93 -0
- package/plugins/reselect-5/NOTICE +51 -0
- package/plugins/reselect-5/gemini-extension.json +1 -0
- package/plugins/reselect-5/plugin.json +10 -0
- package/plugins/reselect-5/skills/reselect/SKILL.md +215 -0
- package/plugins/responselike-4/.claude-plugin/plugin.json +9 -0
- package/plugins/responselike-4/LICENSE +93 -0
- package/plugins/responselike-4/NOTICE +51 -0
- package/plugins/responselike-4/gemini-extension.json +1 -0
- package/plugins/responselike-4/plugin.json +10 -0
- package/plugins/responselike-4/skills/responselike/SKILL.md +173 -0
- package/plugins/rolldown-1/.claude-plugin/plugin.json +9 -0
- package/plugins/rolldown-1/LICENSE +93 -0
- package/plugins/rolldown-1/NOTICE +51 -0
- package/plugins/rolldown-1/gemini-extension.json +1 -0
- package/plugins/rolldown-1/plugin.json +10 -0
- package/plugins/rolldown-1/skills/rolldown/SKILL.md +182 -0
- package/plugins/semver-7/.claude-plugin/plugin.json +9 -0
- package/plugins/semver-7/LICENSE +93 -0
- package/plugins/semver-7/NOTICE +51 -0
- package/plugins/semver-7/gemini-extension.json +1 -0
- package/plugins/semver-7/plugin.json +10 -0
- package/plugins/semver-7/skills/semver/SKILL.md +55 -0
- package/plugins/serde-json-1/.claude-plugin/plugin.json +9 -0
- package/plugins/serde-json-1/LICENSE +93 -0
- package/plugins/serde-json-1/NOTICE +51 -0
- package/plugins/serde-json-1/gemini-extension.json +1 -0
- package/plugins/serde-json-1/plugin.json +10 -0
- package/plugins/serde-json-1/skills/serde-json/SKILL.md +68 -0
- package/plugins/sinon-22/.claude-plugin/plugin.json +9 -0
- package/plugins/sinon-22/LICENSE +93 -0
- package/plugins/sinon-22/NOTICE +51 -0
- package/plugins/sinon-22/gemini-extension.json +1 -0
- package/plugins/sinon-22/plugin.json +10 -0
- package/plugins/sinon-22/skills/sinon/SKILL.md +215 -0
- package/plugins/slice-ansi-9/.claude-plugin/plugin.json +9 -0
- package/plugins/slice-ansi-9/LICENSE +93 -0
- package/plugins/slice-ansi-9/NOTICE +51 -0
- package/plugins/slice-ansi-9/gemini-extension.json +1 -0
- package/plugins/slice-ansi-9/plugin.json +10 -0
- package/plugins/slice-ansi-9/skills/slice-ansi/SKILL.md +111 -0
- package/plugins/smol-toml-1/.claude-plugin/plugin.json +9 -0
- package/plugins/smol-toml-1/LICENSE +93 -0
- package/plugins/smol-toml-1/NOTICE +51 -0
- package/plugins/smol-toml-1/gemini-extension.json +1 -0
- package/plugins/smol-toml-1/plugin.json +10 -0
- package/plugins/smol-toml-1/skills/smol-toml/SKILL.md +63 -0
- package/plugins/socks-proxy-agent-10/.claude-plugin/plugin.json +9 -0
- package/plugins/socks-proxy-agent-10/LICENSE +93 -0
- package/plugins/socks-proxy-agent-10/NOTICE +51 -0
- package/plugins/socks-proxy-agent-10/gemini-extension.json +1 -0
- package/plugins/socks-proxy-agent-10/plugin.json +10 -0
- package/plugins/socks-proxy-agent-10/skills/socks-proxy-agent/SKILL.md +172 -0
- package/plugins/string-width-8/.claude-plugin/plugin.json +9 -0
- package/plugins/string-width-8/LICENSE +93 -0
- package/plugins/string-width-8/NOTICE +51 -0
- package/plugins/string-width-8/gemini-extension.json +1 -0
- package/plugins/string-width-8/plugin.json +10 -0
- package/plugins/string-width-8/skills/string-width/SKILL.md +101 -0
- package/plugins/superstruct-2/.claude-plugin/plugin.json +9 -0
- package/plugins/superstruct-2/LICENSE +93 -0
- package/plugins/superstruct-2/NOTICE +51 -0
- package/plugins/superstruct-2/gemini-extension.json +1 -0
- package/plugins/superstruct-2/plugin.json +10 -0
- package/plugins/superstruct-2/skills/superstruct/SKILL.md +170 -0
- package/plugins/supports-color-11/.claude-plugin/plugin.json +9 -0
- package/plugins/supports-color-11/LICENSE +93 -0
- package/plugins/supports-color-11/NOTICE +51 -0
- package/plugins/supports-color-11/gemini-extension.json +1 -0
- package/plugins/supports-color-11/plugin.json +10 -0
- package/plugins/supports-color-11/skills/supports-color/SKILL.md +115 -0
- package/plugins/tailwindcss-3/.claude-plugin/plugin.json +9 -0
- package/plugins/tailwindcss-3/LICENSE +93 -0
- package/plugins/tailwindcss-3/NOTICE +51 -0
- package/plugins/tailwindcss-3/gemini-extension.json +1 -0
- package/plugins/tailwindcss-3/plugin.json +10 -0
- package/plugins/tailwindcss-3/skills/tailwindcss/SKILL.md +72 -0
- package/plugins/tailwindcss-4/.claude-plugin/plugin.json +9 -0
- package/plugins/tailwindcss-4/LICENSE +93 -0
- package/plugins/tailwindcss-4/NOTICE +51 -0
- package/plugins/tailwindcss-4/gemini-extension.json +1 -0
- package/plugins/tailwindcss-4/plugin.json +10 -0
- package/plugins/tailwindcss-4/skills/tailwindcss/SKILL.md +82 -0
- package/plugins/tanstack-react-router-1/.claude-plugin/plugin.json +9 -0
- package/plugins/tanstack-react-router-1/LICENSE +93 -0
- package/plugins/tanstack-react-router-1/NOTICE +51 -0
- package/plugins/tanstack-react-router-1/gemini-extension.json +1 -0
- package/plugins/tanstack-react-router-1/plugin.json +10 -0
- package/plugins/tanstack-react-router-1/skills/tanstack-react-router/SKILL.md +96 -0
- package/plugins/tedious-20/.claude-plugin/plugin.json +9 -0
- package/plugins/tedious-20/LICENSE +93 -0
- package/plugins/tedious-20/NOTICE +51 -0
- package/plugins/tedious-20/gemini-extension.json +1 -0
- package/plugins/tedious-20/plugin.json +10 -0
- package/plugins/tedious-20/skills/tedious/SKILL.md +174 -0
- package/plugins/testing-library-react-16/.claude-plugin/plugin.json +9 -0
- package/plugins/testing-library-react-16/LICENSE +93 -0
- package/plugins/testing-library-react-16/NOTICE +51 -0
- package/plugins/testing-library-react-16/gemini-extension.json +1 -0
- package/plugins/testing-library-react-16/plugin.json +10 -0
- package/plugins/testing-library-react-16/skills/testing-library-react/SKILL.md +92 -0
- package/plugins/tinyspy-4/.claude-plugin/plugin.json +9 -0
- package/plugins/tinyspy-4/LICENSE +93 -0
- package/plugins/tinyspy-4/NOTICE +51 -0
- package/plugins/tinyspy-4/gemini-extension.json +1 -0
- package/plugins/tinyspy-4/plugin.json +10 -0
- package/plugins/tinyspy-4/skills/tinyspy/SKILL.md +158 -0
- package/plugins/tldts-7/.claude-plugin/plugin.json +9 -0
- package/plugins/tldts-7/LICENSE +93 -0
- package/plugins/tldts-7/NOTICE +51 -0
- package/plugins/tldts-7/gemini-extension.json +1 -0
- package/plugins/tldts-7/plugin.json +10 -0
- package/plugins/tldts-7/skills/tldts/SKILL.md +120 -0
- package/plugins/ts-morph-28/.claude-plugin/plugin.json +9 -0
- package/plugins/ts-morph-28/LICENSE +93 -0
- package/plugins/ts-morph-28/NOTICE +51 -0
- package/plugins/ts-morph-28/gemini-extension.json +1 -0
- package/plugins/ts-morph-28/plugin.json +10 -0
- package/plugins/ts-morph-28/skills/ts-morph/SKILL.md +219 -0
- package/plugins/tsup-8/.claude-plugin/plugin.json +9 -0
- package/plugins/tsup-8/LICENSE +93 -0
- package/plugins/tsup-8/NOTICE +51 -0
- package/plugins/tsup-8/gemini-extension.json +1 -0
- package/plugins/tsup-8/plugin.json +10 -0
- package/plugins/tsup-8/skills/tsup/SKILL.md +97 -0
- package/plugins/tsx-4/.claude-plugin/plugin.json +9 -0
- package/plugins/tsx-4/LICENSE +93 -0
- package/plugins/tsx-4/NOTICE +51 -0
- package/plugins/tsx-4/gemini-extension.json +1 -0
- package/plugins/tsx-4/plugin.json +10 -0
- package/plugins/tsx-4/skills/tsx/SKILL.md +159 -0
- package/plugins/type-fest-5/.claude-plugin/plugin.json +9 -0
- package/plugins/type-fest-5/LICENSE +93 -0
- package/plugins/type-fest-5/NOTICE +51 -0
- package/plugins/type-fest-5/gemini-extension.json +1 -0
- package/plugins/type-fest-5/plugin.json +10 -0
- package/plugins/type-fest-5/skills/type-fest/SKILL.md +132 -0
- package/plugins/typescript-5/.claude-plugin/plugin.json +9 -0
- package/plugins/typescript-5/LICENSE +93 -0
- package/plugins/typescript-5/NOTICE +51 -0
- package/plugins/typescript-5/gemini-extension.json +1 -0
- package/plugins/typescript-5/plugin.json +10 -0
- package/plugins/typescript-5/skills/typescript/SKILL.md +107 -0
- package/plugins/typescript-eslint-8/.claude-plugin/plugin.json +9 -0
- package/plugins/typescript-eslint-8/LICENSE +93 -0
- package/plugins/typescript-eslint-8/NOTICE +51 -0
- package/plugins/typescript-eslint-8/gemini-extension.json +1 -0
- package/plugins/typescript-eslint-8/plugin.json +10 -0
- package/plugins/typescript-eslint-8/skills/typescript-eslint/SKILL.md +179 -0
- package/plugins/typescript-eslint-eslint-plugin-8/.claude-plugin/plugin.json +9 -0
- package/plugins/typescript-eslint-eslint-plugin-8/LICENSE +93 -0
- package/plugins/typescript-eslint-eslint-plugin-8/NOTICE +51 -0
- package/plugins/typescript-eslint-eslint-plugin-8/gemini-extension.json +1 -0
- package/plugins/typescript-eslint-eslint-plugin-8/plugin.json +10 -0
- package/plugins/typescript-eslint-eslint-plugin-8/skills/typescript-eslint-eslint-plugin/SKILL.md +158 -0
- package/plugins/typescript-eslint-estree-8/.claude-plugin/plugin.json +9 -0
- package/plugins/typescript-eslint-estree-8/LICENSE +93 -0
- package/plugins/typescript-eslint-estree-8/NOTICE +51 -0
- package/plugins/typescript-eslint-estree-8/gemini-extension.json +1 -0
- package/plugins/typescript-eslint-estree-8/plugin.json +10 -0
- package/plugins/typescript-eslint-estree-8/skills/typescript-eslint-estree/SKILL.md +125 -0
- package/plugins/typescript-eslint-parser-8/.claude-plugin/plugin.json +9 -0
- package/plugins/typescript-eslint-parser-8/LICENSE +93 -0
- package/plugins/typescript-eslint-parser-8/NOTICE +51 -0
- package/plugins/typescript-eslint-parser-8/gemini-extension.json +1 -0
- package/plugins/typescript-eslint-parser-8/plugin.json +10 -0
- package/plugins/typescript-eslint-parser-8/skills/typescript-eslint-parser/SKILL.md +141 -0
- package/plugins/typescript-eslint-types-8/.claude-plugin/plugin.json +9 -0
- package/plugins/typescript-eslint-types-8/LICENSE +93 -0
- package/plugins/typescript-eslint-types-8/NOTICE +51 -0
- package/plugins/typescript-eslint-types-8/gemini-extension.json +1 -0
- package/plugins/typescript-eslint-types-8/plugin.json +10 -0
- package/plugins/typescript-eslint-types-8/skills/typescript-eslint-types/SKILL.md +117 -0
- package/plugins/typescript-eslint-utils-8/.claude-plugin/plugin.json +9 -0
- package/plugins/typescript-eslint-utils-8/LICENSE +93 -0
- package/plugins/typescript-eslint-utils-8/NOTICE +51 -0
- package/plugins/typescript-eslint-utils-8/gemini-extension.json +1 -0
- package/plugins/typescript-eslint-utils-8/plugin.json +10 -0
- package/plugins/typescript-eslint-utils-8/skills/typescript-eslint-utils/SKILL.md +195 -0
- package/plugins/typescript-eslint-visitor-keys-8/.claude-plugin/plugin.json +9 -0
- package/plugins/typescript-eslint-visitor-keys-8/LICENSE +93 -0
- package/plugins/typescript-eslint-visitor-keys-8/NOTICE +51 -0
- package/plugins/typescript-eslint-visitor-keys-8/gemini-extension.json +1 -0
- package/plugins/typescript-eslint-visitor-keys-8/plugin.json +10 -0
- package/plugins/typescript-eslint-visitor-keys-8/skills/typescript-eslint-visitor-keys/SKILL.md +116 -0
- package/plugins/valibot-1/.claude-plugin/plugin.json +9 -0
- package/plugins/valibot-1/LICENSE +93 -0
- package/plugins/valibot-1/NOTICE +51 -0
- package/plugins/valibot-1/gemini-extension.json +1 -0
- package/plugins/valibot-1/plugin.json +10 -0
- package/plugins/valibot-1/skills/valibot/SKILL.md +190 -0
- package/plugins/vite-7/.claude-plugin/plugin.json +9 -0
- package/plugins/vite-7/LICENSE +93 -0
- package/plugins/vite-7/NOTICE +51 -0
- package/plugins/vite-7/gemini-extension.json +1 -0
- package/plugins/vite-7/plugin.json +10 -0
- package/plugins/vite-7/skills/vite/SKILL.md +111 -0
- package/plugins/vite-8/.claude-plugin/plugin.json +9 -0
- package/plugins/vite-8/LICENSE +93 -0
- package/plugins/vite-8/NOTICE +51 -0
- package/plugins/vite-8/gemini-extension.json +1 -0
- package/plugins/vite-8/plugin.json +10 -0
- package/plugins/vite-8/skills/vite/SKILL.md +267 -0
- package/plugins/vitejs-plugin-react-5/.claude-plugin/plugin.json +9 -0
- package/plugins/vitejs-plugin-react-5/LICENSE +93 -0
- package/plugins/vitejs-plugin-react-5/NOTICE +51 -0
- package/plugins/vitejs-plugin-react-5/gemini-extension.json +1 -0
- package/plugins/vitejs-plugin-react-5/plugin.json +10 -0
- package/plugins/vitejs-plugin-react-5/skills/vitejs-plugin-react/SKILL.md +67 -0
- package/plugins/vitejs-plugin-react-6/.claude-plugin/plugin.json +9 -0
- package/plugins/vitejs-plugin-react-6/LICENSE +93 -0
- package/plugins/vitejs-plugin-react-6/NOTICE +51 -0
- package/plugins/vitejs-plugin-react-6/gemini-extension.json +1 -0
- package/plugins/vitejs-plugin-react-6/plugin.json +10 -0
- package/plugins/vitejs-plugin-react-6/skills/vitejs-plugin-react/SKILL.md +232 -0
- package/plugins/vitest-4/.claude-plugin/plugin.json +9 -0
- package/plugins/vitest-4/LICENSE +93 -0
- package/plugins/vitest-4/NOTICE +51 -0
- package/plugins/vitest-4/gemini-extension.json +1 -0
- package/plugins/vitest-4/plugin.json +10 -0
- package/plugins/vitest-4/skills/vitest/SKILL.md +127 -0
- package/plugins/vitest-5/.claude-plugin/plugin.json +9 -0
- package/plugins/vitest-5/LICENSE +93 -0
- package/plugins/vitest-5/NOTICE +51 -0
- package/plugins/vitest-5/gemini-extension.json +1 -0
- package/plugins/vitest-5/plugin.json +10 -0
- package/plugins/vitest-5/skills/vitest/SKILL.md +186 -0
- package/plugins/whatwg-mimetype-5/.claude-plugin/plugin.json +9 -0
- package/plugins/whatwg-mimetype-5/LICENSE +93 -0
- package/plugins/whatwg-mimetype-5/NOTICE +51 -0
- package/plugins/whatwg-mimetype-5/gemini-extension.json +1 -0
- package/plugins/whatwg-mimetype-5/plugin.json +10 -0
- package/plugins/whatwg-mimetype-5/skills/whatwg-mimetype/SKILL.md +150 -0
- package/plugins/wrangler-4/.claude-plugin/plugin.json +9 -0
- package/plugins/wrangler-4/LICENSE +93 -0
- package/plugins/wrangler-4/NOTICE +51 -0
- package/plugins/wrangler-4/gemini-extension.json +1 -0
- package/plugins/wrangler-4/plugin.json +10 -0
- package/plugins/wrangler-4/skills/wrangler/SKILL.md +87 -0
- package/plugins/wrap-ansi-10/.claude-plugin/plugin.json +9 -0
- package/plugins/wrap-ansi-10/LICENSE +93 -0
- package/plugins/wrap-ansi-10/NOTICE +51 -0
- package/plugins/wrap-ansi-10/gemini-extension.json +1 -0
- package/plugins/wrap-ansi-10/plugin.json +10 -0
- package/plugins/wrap-ansi-10/skills/wrap-ansi/SKILL.md +119 -0
- package/plugins/yaml-2/.claude-plugin/plugin.json +9 -0
- package/plugins/yaml-2/LICENSE +93 -0
- package/plugins/yaml-2/NOTICE +51 -0
- package/plugins/yaml-2/gemini-extension.json +1 -0
- package/plugins/yaml-2/plugin.json +10 -0
- package/plugins/yaml-2/skills/yaml/SKILL.md +56 -0
- package/plugins/yargs-18/.claude-plugin/plugin.json +9 -0
- package/plugins/yargs-18/LICENSE +93 -0
- package/plugins/yargs-18/NOTICE +51 -0
- package/plugins/yargs-18/gemini-extension.json +1 -0
- package/plugins/yargs-18/plugin.json +10 -0
- package/plugins/yargs-18/skills/yargs/SKILL.md +175 -0
- package/plugins/yargs-parser-22/.claude-plugin/plugin.json +9 -0
- package/plugins/yargs-parser-22/LICENSE +93 -0
- package/plugins/yargs-parser-22/NOTICE +51 -0
- package/plugins/yargs-parser-22/gemini-extension.json +1 -0
- package/plugins/yargs-parser-22/plugin.json +10 -0
- package/plugins/yargs-parser-22/skills/yargs-parser/SKILL.md +179 -0
- package/plugins/yeoman-environment-7/.claude-plugin/plugin.json +9 -0
- package/plugins/yeoman-environment-7/LICENSE +93 -0
- package/plugins/yeoman-environment-7/NOTICE +51 -0
- package/plugins/yeoman-environment-7/gemini-extension.json +1 -0
- package/plugins/yeoman-environment-7/plugin.json +10 -0
- package/plugins/yeoman-environment-7/skills/yeoman-environment/SKILL.md +136 -0
- package/plugins/z-schema-12/.claude-plugin/plugin.json +9 -0
- package/plugins/z-schema-12/LICENSE +93 -0
- package/plugins/z-schema-12/NOTICE +51 -0
- package/plugins/z-schema-12/gemini-extension.json +1 -0
- package/plugins/z-schema-12/plugin.json +10 -0
- package/plugins/z-schema-12/skills/z-schema/SKILL.md +179 -0
- package/plugins/zod-schema-validation-3/.claude-plugin/plugin.json +9 -0
- package/plugins/zod-schema-validation-3/LICENSE +93 -0
- package/plugins/zod-schema-validation-3/NOTICE +51 -0
- package/plugins/zod-schema-validation-3/gemini-extension.json +1 -0
- package/plugins/zod-schema-validation-3/plugin.json +10 -0
- package/plugins/zod-schema-validation-3/skills/zod-schema-validation/SKILL.md +188 -0
- package/plugins/zod-schema-validation-4/.claude-plugin/plugin.json +9 -0
- package/plugins/zod-schema-validation-4/LICENSE +93 -0
- package/plugins/zod-schema-validation-4/NOTICE +51 -0
- package/plugins/zod-schema-validation-4/gemini-extension.json +1 -0
- package/plugins/zod-schema-validation-4/plugin.json +10 -0
- package/plugins/zod-schema-validation-4/skills/zod-schema-validation/SKILL.md +181 -0
- package/plugins/zod-validation-error-5/.claude-plugin/plugin.json +9 -0
- package/plugins/zod-validation-error-5/LICENSE +93 -0
- package/plugins/zod-validation-error-5/NOTICE +51 -0
- package/plugins/zod-validation-error-5/gemini-extension.json +1 -0
- package/plugins/zod-validation-error-5/plugin.json +10 -0
- package/plugins/zod-validation-error-5/skills/zod-validation-error/SKILL.md +212 -0
- package/plugins/zustand-5/.claude-plugin/plugin.json +9 -0
- package/plugins/zustand-5/LICENSE +93 -0
- package/plugins/zustand-5/NOTICE +51 -0
- package/plugins/zustand-5/gemini-extension.json +1 -0
- package/plugins/zustand-5/plugin.json +10 -0
- package/plugins/zustand-5/skills/zustand/SKILL.md +128 -0
- package/skills/package-skill-catalog/SKILL.md +37 -222
package/dist/cli.js
CHANGED
|
@@ -11,19 +11,19 @@ var __export = (target2, all) => {
|
|
|
11
11
|
|
|
12
12
|
// src/storage/sqlite.ts
|
|
13
13
|
async function openSqlite(path, options2 = {}) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
const provider = process.versions.bun ? "bun:sqlite" : "node:sqlite";
|
|
15
|
+
const sqlite = await import(provider);
|
|
16
|
+
if (provider === "node:sqlite") {
|
|
17
|
+
const { DatabaseSync } = sqlite;
|
|
18
|
+
return { db: new DatabaseSync(path, options2), provider };
|
|
18
19
|
}
|
|
19
|
-
const
|
|
20
|
-
const { Database } = await import(name);
|
|
20
|
+
const { Database } = sqlite;
|
|
21
21
|
const native2 = new Database(path, {
|
|
22
22
|
create: !options2.readOnly,
|
|
23
23
|
readonly: options2.readOnly
|
|
24
24
|
});
|
|
25
25
|
return {
|
|
26
|
-
provider
|
|
26
|
+
provider,
|
|
27
27
|
db: {
|
|
28
28
|
exec: (sql) => native2.run(sql),
|
|
29
29
|
prepare: (sql) => native2.query(sql),
|
|
@@ -159,8 +159,8 @@ var init_encoder = __esm({
|
|
|
159
159
|
});
|
|
160
160
|
|
|
161
161
|
// src/config.ts
|
|
162
|
-
import { existsSync as
|
|
163
|
-
import { dirname as dirname3, join as
|
|
162
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
|
|
163
|
+
import { dirname as dirname3, join as join4, resolve as resolve4 } from "path";
|
|
164
164
|
import { parse as parse2 } from "smol-toml";
|
|
165
165
|
function embeddingSection(path, dir, value) {
|
|
166
166
|
if (value === void 0) return void 0;
|
|
@@ -227,8 +227,8 @@ function embeddingSection(path, dir, value) {
|
|
|
227
227
|
revision: string("revision"),
|
|
228
228
|
encoder,
|
|
229
229
|
enabled: section.enabled,
|
|
230
|
-
modelPath: modelPath === void 0 ? void 0 :
|
|
231
|
-
cacheDir: cacheDir === void 0 ? void 0 :
|
|
230
|
+
modelPath: modelPath === void 0 ? void 0 : resolve4(dir, modelPath),
|
|
231
|
+
cacheDir: cacheDir === void 0 ? void 0 : resolve4(dir, cacheDir)
|
|
232
232
|
};
|
|
233
233
|
try {
|
|
234
234
|
resolveEncoder(result.modelId, result);
|
|
@@ -241,10 +241,10 @@ function embeddingSection(path, dir, value) {
|
|
|
241
241
|
return result;
|
|
242
242
|
}
|
|
243
243
|
function findConfig(start2) {
|
|
244
|
-
let dir =
|
|
244
|
+
let dir = resolve4(start2);
|
|
245
245
|
for (; ; ) {
|
|
246
|
-
const candidate2 =
|
|
247
|
-
if (
|
|
246
|
+
const candidate2 = join4(dir, CONFIG_FILE);
|
|
247
|
+
if (existsSync4(candidate2)) return candidate2;
|
|
248
248
|
const parent = dirname3(dir);
|
|
249
249
|
if (parent === dir) return null;
|
|
250
250
|
dir = parent;
|
|
@@ -257,11 +257,11 @@ function stringList(path, key, value) {
|
|
|
257
257
|
return value;
|
|
258
258
|
}
|
|
259
259
|
function readConfig(path) {
|
|
260
|
-
const absolute2 =
|
|
260
|
+
const absolute2 = resolve4(path);
|
|
261
261
|
const dir = dirname3(absolute2);
|
|
262
262
|
let raw;
|
|
263
263
|
try {
|
|
264
|
-
raw = parse2(
|
|
264
|
+
raw = parse2(readFileSync4(absolute2, "utf8"));
|
|
265
265
|
} catch (error) {
|
|
266
266
|
const reason = error instanceof Error ? error.message : "unreadable";
|
|
267
267
|
throw new ConfigError(absolute2, reason.split("\n")[0] ?? reason);
|
|
@@ -279,10 +279,22 @@ function readConfig(path) {
|
|
|
279
279
|
if (!CATALOG_KEYS.has(key)) {
|
|
280
280
|
throw new ConfigError(absolute2, `unknown key [catalog] ${key}`);
|
|
281
281
|
}
|
|
282
|
+
if (key === "builtin") {
|
|
283
|
+
if (typeof value !== "boolean")
|
|
284
|
+
throw new ConfigError(absolute2, "[catalog] builtin must be a boolean");
|
|
285
|
+
catalog[key] = value;
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
282
288
|
const list2 = stringList(absolute2, key, value);
|
|
283
|
-
catalog[key] = key === "scopes" ? list2 : list2.map((p) =>
|
|
289
|
+
catalog[key] = key === "scopes" ? list2 : list2.map((p) => resolve4(dir, p));
|
|
284
290
|
}
|
|
285
|
-
return {
|
|
291
|
+
return {
|
|
292
|
+
path: absolute2,
|
|
293
|
+
dir,
|
|
294
|
+
catalog,
|
|
295
|
+
raw,
|
|
296
|
+
...configured
|
|
297
|
+
};
|
|
286
298
|
}
|
|
287
299
|
var CONFIG_FILE, ConfigError, PATH_KEYS, CATALOG_KEYS;
|
|
288
300
|
var init_config = __esm({
|
|
@@ -297,7 +309,11 @@ var init_config = __esm({
|
|
|
297
309
|
}
|
|
298
310
|
};
|
|
299
311
|
PATH_KEYS = ["roots", "workspaces", "packages", "marketplaces"];
|
|
300
|
-
CATALOG_KEYS = /* @__PURE__ */ new Set([
|
|
312
|
+
CATALOG_KEYS = /* @__PURE__ */ new Set([
|
|
313
|
+
...PATH_KEYS,
|
|
314
|
+
"scopes",
|
|
315
|
+
"builtin"
|
|
316
|
+
]);
|
|
301
317
|
}
|
|
302
318
|
});
|
|
303
319
|
|
|
@@ -448,17 +464,17 @@ import { randomUUID } from "crypto";
|
|
|
448
464
|
import {
|
|
449
465
|
closeSync,
|
|
450
466
|
constants,
|
|
451
|
-
existsSync as
|
|
467
|
+
existsSync as existsSync7,
|
|
452
468
|
fstatSync,
|
|
453
469
|
mkdirSync as mkdirSync2,
|
|
454
470
|
openSync,
|
|
455
|
-
readFileSync as
|
|
471
|
+
readFileSync as readFileSync11,
|
|
456
472
|
renameSync,
|
|
457
473
|
rmSync,
|
|
458
474
|
writeFileSync
|
|
459
475
|
} from "fs";
|
|
460
476
|
import { homedir } from "os";
|
|
461
|
-
import { dirname as
|
|
477
|
+
import { dirname as dirname9, join as join9, resolve as resolve7 } from "path";
|
|
462
478
|
import { z as z12 } from "zod";
|
|
463
479
|
function mailboxConfigFrom(project2, env = process.env) {
|
|
464
480
|
if (project2.raw.mailbox === void 0) return void 0;
|
|
@@ -466,17 +482,17 @@ function mailboxConfigFrom(project2, env = process.env) {
|
|
|
466
482
|
if (!parsed.success)
|
|
467
483
|
throw new MailboxError("invalid_config", "Invalid [mailbox] configuration");
|
|
468
484
|
const section = parsed.data;
|
|
469
|
-
const stateRoot = env.XDG_STATE_HOME ??
|
|
470
|
-
const stateDir = section.state_dir ?
|
|
485
|
+
const stateRoot = env.XDG_STATE_HOME ?? join9(homedir(), ".local/state");
|
|
486
|
+
const stateDir = section.state_dir ? resolve7(project2.dir, section.state_dir) : join9(stateRoot, "pmcp/projects", section.project_id);
|
|
471
487
|
return {
|
|
472
488
|
projectId: section.project_id,
|
|
473
489
|
enabled: section.enabled,
|
|
474
|
-
databasePath:
|
|
490
|
+
databasePath: join9(stateDir, "mailbox.sqlite")
|
|
475
491
|
};
|
|
476
492
|
}
|
|
477
493
|
function initializeMailboxConfig(file2, stateDirectory2) {
|
|
478
|
-
const path =
|
|
479
|
-
const original =
|
|
494
|
+
const path = resolve7(file2);
|
|
495
|
+
const original = existsSync7(path) ? readFileSync11(path, "utf8") : void 0;
|
|
480
496
|
if (original !== void 0 && readConfig(path).raw.mailbox !== void 0) {
|
|
481
497
|
throw new MailboxError("mailbox_exists", "Mailbox is already configured");
|
|
482
498
|
}
|
|
@@ -491,14 +507,14 @@ function initializeMailboxConfig(file2, stateDirectory2) {
|
|
|
491
507
|
const text3 = `${original ?? ""}
|
|
492
508
|
${lines2.join("\n")}
|
|
493
509
|
`;
|
|
494
|
-
mkdirSync2(
|
|
510
|
+
mkdirSync2(dirname9(path), { recursive: true });
|
|
495
511
|
if (original === void 0) {
|
|
496
512
|
writeFileSync(path, text3, { flag: "wx", mode: 384 });
|
|
497
513
|
} else {
|
|
498
514
|
const temporary = `${path}.${randomUUID()}.tmp`;
|
|
499
515
|
try {
|
|
500
516
|
writeFileSync(temporary, text3, { flag: "wx", mode: 384 });
|
|
501
|
-
if (
|
|
517
|
+
if (readFileSync11(path, "utf8") !== original) {
|
|
502
518
|
throw new MailboxError(
|
|
503
519
|
"config_conflict",
|
|
504
520
|
"Configuration changed during initialization"
|
|
@@ -527,7 +543,7 @@ function readPrivateJson(file2) {
|
|
|
527
543
|
"Credential file must belong to the current user"
|
|
528
544
|
);
|
|
529
545
|
}
|
|
530
|
-
return JSON.parse(
|
|
546
|
+
return JSON.parse(readFileSync11(descriptor, "utf8"));
|
|
531
547
|
} finally {
|
|
532
548
|
closeSync(descriptor);
|
|
533
549
|
}
|
|
@@ -539,7 +555,7 @@ function readBinding(file2) {
|
|
|
539
555
|
return bindingSchema.parse(readPrivateJson(file2));
|
|
540
556
|
}
|
|
541
557
|
function writePrivateJson(file2, value) {
|
|
542
|
-
mkdirSync2(
|
|
558
|
+
mkdirSync2(dirname9(resolve7(file2)), { recursive: true, mode: 448 });
|
|
543
559
|
writeFileSync(file2, `${JSON.stringify(value, null, 2)}
|
|
544
560
|
`, {
|
|
545
561
|
flag: "wx",
|
|
@@ -547,7 +563,7 @@ function writePrivateJson(file2, value) {
|
|
|
547
563
|
});
|
|
548
564
|
}
|
|
549
565
|
function writeBindingFile(file2, binding) {
|
|
550
|
-
if (
|
|
566
|
+
if (existsSync7(file2)) {
|
|
551
567
|
const previous = readBinding(file2);
|
|
552
568
|
if (previous.projectId !== binding.projectId || previous.actorId !== binding.actorId) {
|
|
553
569
|
throw new MailboxError(
|
|
@@ -725,7 +741,7 @@ UPDATE mailbox_actors SET authority_expires_at_ms = created_at_ms + 60000 WHERE
|
|
|
725
741
|
|
|
726
742
|
// src/mailbox/sqlite.ts
|
|
727
743
|
import { chmodSync, mkdirSync as mkdirSync3 } from "fs";
|
|
728
|
-
import { dirname as
|
|
744
|
+
import { dirname as dirname10 } from "path";
|
|
729
745
|
import { setTimeout as delay } from "timers/promises";
|
|
730
746
|
function storageError(error) {
|
|
731
747
|
const detail = error instanceof Error ? error.message : "";
|
|
@@ -744,7 +760,7 @@ async function openDatabase2(file2) {
|
|
|
744
760
|
let native2;
|
|
745
761
|
try {
|
|
746
762
|
if (file2 !== ":memory:")
|
|
747
|
-
mkdirSync3(
|
|
763
|
+
mkdirSync3(dirname10(file2), { recursive: true, mode: 448 });
|
|
748
764
|
native2 = (await openSqlite(file2)).db;
|
|
749
765
|
if (file2 !== ":memory:") chmodSync(file2, 384);
|
|
750
766
|
} catch (error) {
|
|
@@ -812,7 +828,7 @@ import {
|
|
|
812
828
|
chmodSync as chmodSync2,
|
|
813
829
|
copyFileSync,
|
|
814
830
|
constants as constants2,
|
|
815
|
-
existsSync as
|
|
831
|
+
existsSync as existsSync8,
|
|
816
832
|
unlinkSync
|
|
817
833
|
} from "fs";
|
|
818
834
|
function hash(secret2) {
|
|
@@ -838,8 +854,8 @@ async function restoreBackup(source2, config) {
|
|
|
838
854
|
probe.close();
|
|
839
855
|
}
|
|
840
856
|
store = await MailboxStore.open(config);
|
|
841
|
-
const
|
|
842
|
-
if (
|
|
857
|
+
const integrity2 = store.database.get("PRAGMA integrity_check");
|
|
858
|
+
if (integrity2?.integrity_check !== "ok" || store.database.all("PRAGMA foreign_key_check").length > 0) {
|
|
843
859
|
throw new MailboxError(
|
|
844
860
|
"storage_corrupt",
|
|
845
861
|
"Restored backup failed integrity checks"
|
|
@@ -1256,7 +1272,7 @@ var init_store = __esm({
|
|
|
1256
1272
|
"Mailbox storage is closed"
|
|
1257
1273
|
);
|
|
1258
1274
|
}
|
|
1259
|
-
if (
|
|
1275
|
+
if (existsSync8(destination)) {
|
|
1260
1276
|
throw new MailboxError(
|
|
1261
1277
|
"destination_exists",
|
|
1262
1278
|
"Backup destination already exists"
|
|
@@ -1580,7 +1596,7 @@ var init_service = __esm({
|
|
|
1580
1596
|
});
|
|
1581
1597
|
|
|
1582
1598
|
// src/harness/validation.ts
|
|
1583
|
-
function
|
|
1599
|
+
function object8(value) {
|
|
1584
1600
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
1585
1601
|
throw new Error("invalid_object");
|
|
1586
1602
|
}
|
|
@@ -1665,11 +1681,11 @@ var init_layout = __esm({
|
|
|
1665
1681
|
});
|
|
1666
1682
|
|
|
1667
1683
|
// src/harness/adapters/process/artifact.ts
|
|
1668
|
-
import { fileURLToPath as
|
|
1684
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
1669
1685
|
function artifact(name) {
|
|
1670
1686
|
if (import.meta.url.endsWith(".ts"))
|
|
1671
|
-
return
|
|
1672
|
-
return
|
|
1687
|
+
return fileURLToPath3(new URL(sources[name], import.meta.url));
|
|
1688
|
+
return fileURLToPath3(new URL(`./harness-${name}.js`, import.meta.url));
|
|
1673
1689
|
}
|
|
1674
1690
|
var sources;
|
|
1675
1691
|
var init_artifact = __esm({
|
|
@@ -1700,9 +1716,9 @@ var init_registry = __esm({
|
|
|
1700
1716
|
});
|
|
1701
1717
|
|
|
1702
1718
|
// src/harness/runtimes/egress-policy.ts
|
|
1703
|
-
import { isAbsolute as
|
|
1719
|
+
import { isAbsolute as isAbsolute8, posix as posix3 } from "path";
|
|
1704
1720
|
function egressPolicy(value) {
|
|
1705
|
-
const policy =
|
|
1721
|
+
const policy = object8(value);
|
|
1706
1722
|
const hosts = strings2(policy.hosts, "egress_hosts").map(
|
|
1707
1723
|
(host) => host.toLowerCase()
|
|
1708
1724
|
);
|
|
@@ -1716,7 +1732,7 @@ function egressPolicy(value) {
|
|
|
1716
1732
|
}
|
|
1717
1733
|
if (policy.bridgeExecutable === void 0) return { hosts };
|
|
1718
1734
|
const executable = text(policy.bridgeExecutable, "egress_bridge_executable");
|
|
1719
|
-
if (!
|
|
1735
|
+
if (!isAbsolute8(executable) || posix3.normalize(executable) !== executable || /[\r\n\x1b]/.test(executable))
|
|
1720
1736
|
throw new Error("invalid_egress_bridge_executable");
|
|
1721
1737
|
return { hosts, bridgeExecutable: executable };
|
|
1722
1738
|
}
|
|
@@ -1728,7 +1744,7 @@ var init_egress_policy = __esm({
|
|
|
1728
1744
|
});
|
|
1729
1745
|
|
|
1730
1746
|
// src/harness/runtimes/private-profiles.ts
|
|
1731
|
-
import { isAbsolute as
|
|
1747
|
+
import { isAbsolute as isAbsolute9, posix as posix4 } from "path";
|
|
1732
1748
|
function profileName(value) {
|
|
1733
1749
|
const name = text(value, "private_profile");
|
|
1734
1750
|
if (!/^[a-z][a-z0-9-]{0,31}$/.test(name))
|
|
@@ -1736,13 +1752,13 @@ function profileName(value) {
|
|
|
1736
1752
|
return name;
|
|
1737
1753
|
}
|
|
1738
1754
|
function privateProfiles(value) {
|
|
1739
|
-
const entries = Object.entries(
|
|
1755
|
+
const entries = Object.entries(object8(value));
|
|
1740
1756
|
if (!entries.length || entries.length > 16)
|
|
1741
1757
|
throw new Error("invalid_private_profile_grants");
|
|
1742
1758
|
const result = {};
|
|
1743
1759
|
for (const [name, entry2] of entries) {
|
|
1744
1760
|
profileName(name);
|
|
1745
|
-
const grant =
|
|
1761
|
+
const grant = object8(entry2);
|
|
1746
1762
|
const runtime2 = text(grant.runtime, "private_profile_runtime");
|
|
1747
1763
|
const directory = absolute(grant.directory, "private_profile_directory");
|
|
1748
1764
|
const lockExecutable = absolute(
|
|
@@ -1765,7 +1781,7 @@ function privateProfiles(value) {
|
|
|
1765
1781
|
}
|
|
1766
1782
|
function absolute(value, field) {
|
|
1767
1783
|
const path = text(value, field);
|
|
1768
|
-
if (!
|
|
1784
|
+
if (!isAbsolute9(path) || posix4.normalize(path) !== path || /[\r\n\x1b]/.test(path))
|
|
1769
1785
|
throw new Error("invalid_private_profile_path");
|
|
1770
1786
|
return path;
|
|
1771
1787
|
}
|
|
@@ -1784,9 +1800,9 @@ var init_private_profiles = __esm({
|
|
|
1784
1800
|
});
|
|
1785
1801
|
|
|
1786
1802
|
// src/harness/runtimes/docker-policy.ts
|
|
1787
|
-
import { isAbsolute as
|
|
1803
|
+
import { isAbsolute as isAbsolute10, posix as posix5 } from "path";
|
|
1788
1804
|
function dockerPolicy(value) {
|
|
1789
|
-
const policy =
|
|
1805
|
+
const policy = object8(value);
|
|
1790
1806
|
if (policy.schemaVersion !== 1)
|
|
1791
1807
|
throw new Error("invalid_docker_policy_version");
|
|
1792
1808
|
if (!/^sha256:[a-f0-9]{64}$/.test(text(policy.image, "docker_image")))
|
|
@@ -1795,7 +1811,7 @@ function dockerPolicy(value) {
|
|
|
1795
1811
|
throw new Error("docker_network_not_qualified");
|
|
1796
1812
|
if (policy.workspaceAccess !== "read-only" && policy.workspaceAccess !== "read-write")
|
|
1797
1813
|
throw new Error("invalid_docker_workspace_access");
|
|
1798
|
-
const commands =
|
|
1814
|
+
const commands = object8(policy.commands);
|
|
1799
1815
|
const entries = Object.entries(commands);
|
|
1800
1816
|
if (!entries.length || entries.length > 5)
|
|
1801
1817
|
throw new Error("invalid_docker_runtime_commands");
|
|
@@ -1805,7 +1821,7 @@ function dockerPolicy(value) {
|
|
|
1805
1821
|
throw new Error("unsupported_docker_runtime");
|
|
1806
1822
|
const command2 = strings2(value2, "docker_runtime_command");
|
|
1807
1823
|
const executable = command2[0];
|
|
1808
|
-
if (!
|
|
1824
|
+
if (!isAbsolute10(executable) || posix5.normalize(executable) !== executable || command2.some((argument) => /[\r\n\x1b]/.test(argument)))
|
|
1809
1825
|
throw new Error("invalid_docker_runtime_command");
|
|
1810
1826
|
parsed[runtime2] = command2;
|
|
1811
1827
|
}
|
|
@@ -1836,15 +1852,15 @@ function member(value, allowed3) {
|
|
|
1836
1852
|
function records(value) {
|
|
1837
1853
|
if (!Array.isArray(value) || value.length > 4096)
|
|
1838
1854
|
throw new Error("invalid_state_collection");
|
|
1839
|
-
return value.map(
|
|
1855
|
+
return value.map(object8);
|
|
1840
1856
|
}
|
|
1841
1857
|
function unique(values) {
|
|
1842
1858
|
if (new Set(values).size !== values.length)
|
|
1843
1859
|
throw new Error("duplicate_state_identity");
|
|
1844
1860
|
}
|
|
1845
1861
|
function validateState(value) {
|
|
1846
|
-
const state =
|
|
1847
|
-
const group =
|
|
1862
|
+
const state = object8(value);
|
|
1863
|
+
const group = object8(state.group);
|
|
1848
1864
|
member(group.schemaVersion, [1]);
|
|
1849
1865
|
for (const field of ["owner", "id", "generation"]) id(group[field], field);
|
|
1850
1866
|
text(group.projectRoot, "project_root");
|
|
@@ -1911,12 +1927,12 @@ import {
|
|
|
1911
1927
|
fstatSync as fstatSync2,
|
|
1912
1928
|
fsyncSync,
|
|
1913
1929
|
openSync as openSync3,
|
|
1914
|
-
readFileSync as
|
|
1915
|
-
realpathSync as
|
|
1930
|
+
readFileSync as readFileSync33,
|
|
1931
|
+
realpathSync as realpathSync4,
|
|
1916
1932
|
renameSync as renameSync3,
|
|
1917
1933
|
writeFileSync as writeFileSync9
|
|
1918
1934
|
} from "fs";
|
|
1919
|
-
import { dirname as
|
|
1935
|
+
import { dirname as dirname19, join as join27 } from "path";
|
|
1920
1936
|
function privateJson(path, value) {
|
|
1921
1937
|
const temporary = `${path}.${randomUUID4()}.new`;
|
|
1922
1938
|
const descriptor = openSync3(temporary, "wx", 384);
|
|
@@ -1928,7 +1944,7 @@ function privateJson(path, value) {
|
|
|
1928
1944
|
closeSync3(descriptor);
|
|
1929
1945
|
}
|
|
1930
1946
|
renameSync3(temporary, path);
|
|
1931
|
-
const directory = openSync3(
|
|
1947
|
+
const directory = openSync3(dirname19(path), "r");
|
|
1932
1948
|
try {
|
|
1933
1949
|
fsyncSync(directory);
|
|
1934
1950
|
} finally {
|
|
@@ -1942,7 +1958,7 @@ function readPrivateJson2(path) {
|
|
|
1942
1958
|
if (!stat.isFile() || (stat.mode & 63) !== 0 || stat.size > 2097152)
|
|
1943
1959
|
throw new Error("unsafe_state_file");
|
|
1944
1960
|
if (stat.uid !== process.getuid?.()) throw new Error("foreign_state_file");
|
|
1945
|
-
return JSON.parse(
|
|
1961
|
+
return JSON.parse(readFileSync33(descriptor, "utf8"));
|
|
1946
1962
|
} finally {
|
|
1947
1963
|
closeSync3(descriptor);
|
|
1948
1964
|
}
|
|
@@ -1957,10 +1973,10 @@ var init_store2 = __esm({
|
|
|
1957
1973
|
path;
|
|
1958
1974
|
state;
|
|
1959
1975
|
constructor(path) {
|
|
1960
|
-
this.path =
|
|
1976
|
+
this.path = realpathSync4(path);
|
|
1961
1977
|
this.state = validateState(readPrivateJson2(path));
|
|
1962
|
-
const metadata2 =
|
|
1963
|
-
readPrivateJson2(
|
|
1978
|
+
const metadata2 = object8(
|
|
1979
|
+
readPrivateJson2(join27(dirname19(this.path), "harness.json"))
|
|
1964
1980
|
);
|
|
1965
1981
|
if (this.state.group.schemaVersion !== 1 || this.state.group.owner !== metadata2.owner) {
|
|
1966
1982
|
throw new Error("foreign_group");
|
|
@@ -1982,12 +1998,12 @@ var init_store2 = __esm({
|
|
|
1982
1998
|
// src/harness/adapters/process/serialized.ts
|
|
1983
1999
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
1984
2000
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
1985
|
-
import { existsSync as
|
|
1986
|
-
import { dirname as
|
|
2001
|
+
import { existsSync as existsSync22, unlinkSync as unlinkSync2 } from "fs";
|
|
2002
|
+
import { dirname as dirname20, join as join28 } from "path";
|
|
1987
2003
|
function serialized(operation) {
|
|
1988
2004
|
const store = new GroupStore(text(operation.groupFile, "group_file"));
|
|
1989
|
-
const directory =
|
|
1990
|
-
const operationFile =
|
|
2005
|
+
const directory = dirname20(store.path);
|
|
2006
|
+
const operationFile = join28(directory, `operation-${randomUUID5()}.json`);
|
|
1991
2007
|
privateJson(operationFile, operation);
|
|
1992
2008
|
const worker = artifact("worker");
|
|
1993
2009
|
try {
|
|
@@ -1999,7 +2015,7 @@ function serialized(operation) {
|
|
|
1999
2015
|
"--no-fork",
|
|
2000
2016
|
"--conflict-exit-code",
|
|
2001
2017
|
"73",
|
|
2002
|
-
|
|
2018
|
+
join28(directory, "group-control.lock"),
|
|
2003
2019
|
process.execPath,
|
|
2004
2020
|
worker,
|
|
2005
2021
|
operationFile
|
|
@@ -2015,7 +2031,7 @@ function serialized(operation) {
|
|
|
2015
2031
|
if (!result.stdout.trim()) throw new Error("worker_receipt_missing");
|
|
2016
2032
|
return JSON.parse(result.stdout);
|
|
2017
2033
|
} finally {
|
|
2018
|
-
if (
|
|
2034
|
+
if (existsSync22(operationFile)) unlinkSync2(operationFile);
|
|
2019
2035
|
}
|
|
2020
2036
|
}
|
|
2021
2037
|
var init_serialized = __esm({
|
|
@@ -2050,9 +2066,9 @@ async function attachNative(spec, renew) {
|
|
|
2050
2066
|
}, 2e4) : void 0;
|
|
2051
2067
|
let exitCode;
|
|
2052
2068
|
try {
|
|
2053
|
-
exitCode = await new Promise((
|
|
2069
|
+
exitCode = await new Promise((resolve22, reject) => {
|
|
2054
2070
|
client.once("error", reject);
|
|
2055
|
-
client.once("exit", (code) =>
|
|
2071
|
+
client.once("exit", (code) => resolve22(code ?? 1));
|
|
2056
2072
|
});
|
|
2057
2073
|
} finally {
|
|
2058
2074
|
if (timer) clearInterval(timer);
|
|
@@ -2095,11 +2111,11 @@ var init_command = __esm({
|
|
|
2095
2111
|
});
|
|
2096
2112
|
|
|
2097
2113
|
// src/harness/adapters/process/identity.ts
|
|
2098
|
-
import { readFileSync as
|
|
2114
|
+
import { readFileSync as readFileSync34 } from "fs";
|
|
2099
2115
|
function processIdentity(pid) {
|
|
2100
2116
|
if (!Number.isSafeInteger(pid) || pid < 1)
|
|
2101
2117
|
throw new Error("invalid_process_id");
|
|
2102
|
-
const stat =
|
|
2118
|
+
const stat = readFileSync34(`/proc/${pid}/stat`, "utf8");
|
|
2103
2119
|
const fields = stat.slice(stat.lastIndexOf(")") + 2).split(" ");
|
|
2104
2120
|
if (!/^\d+$/.test(fields[19] ?? ""))
|
|
2105
2121
|
throw new Error("process_identity_unknown");
|
|
@@ -2139,9 +2155,9 @@ var init_identity2 = __esm({
|
|
|
2139
2155
|
});
|
|
2140
2156
|
|
|
2141
2157
|
// src/harness/adapters/docker/claims.ts
|
|
2142
|
-
import { createHash as
|
|
2158
|
+
import { createHash as createHash15 } from "crypto";
|
|
2143
2159
|
function resourceClaim(value, owner) {
|
|
2144
|
-
const record =
|
|
2160
|
+
const record = object8(value);
|
|
2145
2161
|
if (record.kind !== "docker" || record.owner !== owner)
|
|
2146
2162
|
throw new Error("foreign_container_resource");
|
|
2147
2163
|
if (!/^[a-f0-9]{64}$/.test(text(record.id, "container_id")))
|
|
@@ -2170,23 +2186,23 @@ function resourceClaim(value, owner) {
|
|
|
2170
2186
|
return record;
|
|
2171
2187
|
}
|
|
2172
2188
|
function assertResourceOwner(resource, actual) {
|
|
2173
|
-
const config =
|
|
2174
|
-
const labels =
|
|
2189
|
+
const config = object8(actual.Config);
|
|
2190
|
+
const labels = object8(config.Labels);
|
|
2175
2191
|
if (actual.Id !== resource.id || actual.Image !== resource.image || labels[containerLabels.owner] !== resource.owner || labels[containerLabels.nonce] !== resource.nonce || labels[containerLabels.role] !== resource.role)
|
|
2176
2192
|
throw new Error("foreign_container_identity");
|
|
2177
2193
|
}
|
|
2178
2194
|
function configurationDigest(actual) {
|
|
2179
|
-
const host =
|
|
2195
|
+
const host = object8(actual.HostConfig);
|
|
2180
2196
|
if (host.OomKillDisable !== false && host.OomKillDisable !== null)
|
|
2181
2197
|
throw new Error("container_oom_protection_disabled");
|
|
2182
2198
|
if (!Array.isArray(actual.Mounts))
|
|
2183
2199
|
throw new Error("invalid_container_mounts");
|
|
2184
|
-
const mounts = actual.Mounts.map(
|
|
2200
|
+
const mounts = actual.Mounts.map(object8).sort(
|
|
2185
2201
|
(left, right) => text(left.Destination, "mount_destination").localeCompare(
|
|
2186
2202
|
text(right.Destination, "mount_destination")
|
|
2187
2203
|
)
|
|
2188
2204
|
);
|
|
2189
|
-
return
|
|
2205
|
+
return createHash15("sha256").update(
|
|
2190
2206
|
JSON.stringify(
|
|
2191
2207
|
canonicalValue({
|
|
2192
2208
|
config: actual.Config,
|
|
@@ -2217,7 +2233,7 @@ function inspectResource(resource, enforceConfiguration = true) {
|
|
|
2217
2233
|
const result = JSON.parse(command("docker", ["inspect", resource.id]));
|
|
2218
2234
|
if (!Array.isArray(result) || result.length !== 1)
|
|
2219
2235
|
throw new Error("invalid_container_inspection");
|
|
2220
|
-
const actual =
|
|
2236
|
+
const actual = object8(result[0]);
|
|
2221
2237
|
assertResourceOwner(resource, actual);
|
|
2222
2238
|
if (enforceConfiguration && configurationDigest(actual) !== resource.configurationDigest)
|
|
2223
2239
|
throw new Error("container_configuration_changed");
|
|
@@ -2231,7 +2247,7 @@ function resourceAbsent(error, id2) {
|
|
|
2231
2247
|
);
|
|
2232
2248
|
}
|
|
2233
2249
|
function assertResourceFresh(resource) {
|
|
2234
|
-
const state =
|
|
2250
|
+
const state = object8(inspectResource(resource).State);
|
|
2235
2251
|
if (state.Status !== "created" || state.Running !== false || state.Pid !== 0)
|
|
2236
2252
|
throw new Error("container_requires_fresh_creation");
|
|
2237
2253
|
}
|
|
@@ -2243,7 +2259,7 @@ function resourceRunning(resource) {
|
|
|
2243
2259
|
if (resourceAbsent(error, resource.id)) return false;
|
|
2244
2260
|
throw error;
|
|
2245
2261
|
}
|
|
2246
|
-
const state =
|
|
2262
|
+
const state = object8(actual.State);
|
|
2247
2263
|
if (typeof state.Running !== "boolean")
|
|
2248
2264
|
throw new Error("unknown_container_lifecycle");
|
|
2249
2265
|
return state.Running;
|
|
@@ -2331,17 +2347,17 @@ var init_connection = __esm({
|
|
|
2331
2347
|
// src/harness/adapters/tmux/records.ts
|
|
2332
2348
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
2333
2349
|
import {
|
|
2334
|
-
lstatSync as
|
|
2350
|
+
lstatSync as lstatSync5,
|
|
2335
2351
|
mkdirSync as mkdirSync9,
|
|
2336
|
-
readdirSync as
|
|
2337
|
-
realpathSync as
|
|
2352
|
+
readdirSync as readdirSync10,
|
|
2353
|
+
realpathSync as realpathSync5,
|
|
2338
2354
|
writeFileSync as writeFileSync10
|
|
2339
2355
|
} from "fs";
|
|
2340
2356
|
import { homedir as homedir7 } from "os";
|
|
2341
|
-
import { join as
|
|
2357
|
+
import { join as join29 } from "path";
|
|
2342
2358
|
function stateDirectory() {
|
|
2343
|
-
return
|
|
2344
|
-
process.env.XDG_STATE_HOME ??
|
|
2359
|
+
return join29(
|
|
2360
|
+
process.env.XDG_STATE_HOME ?? join29(homedir7(), ".local/state"),
|
|
2345
2361
|
"pmcp/harness"
|
|
2346
2362
|
);
|
|
2347
2363
|
}
|
|
@@ -2368,17 +2384,17 @@ var init_records = __esm({
|
|
|
2368
2384
|
configPath;
|
|
2369
2385
|
owner;
|
|
2370
2386
|
constructor(directory) {
|
|
2371
|
-
const stat =
|
|
2387
|
+
const stat = lstatSync5(directory);
|
|
2372
2388
|
if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0)
|
|
2373
2389
|
throw new Error("unsafe_group_directory");
|
|
2374
|
-
this.directory =
|
|
2375
|
-
this.socket =
|
|
2390
|
+
this.directory = realpathSync5(directory);
|
|
2391
|
+
this.socket = join29(this.directory, "s");
|
|
2376
2392
|
if (Buffer.byteLength(this.socket) > 100)
|
|
2377
2393
|
throw new Error("socket_path_too_long");
|
|
2378
|
-
this.metadataPath =
|
|
2379
|
-
this.configPath =
|
|
2394
|
+
this.metadataPath = join29(this.directory, "harness.json");
|
|
2395
|
+
this.configPath = join29(this.directory, "tmux.conf");
|
|
2380
2396
|
this.owner = text(
|
|
2381
|
-
|
|
2397
|
+
object8(readPrivateJson2(this.metadataPath)).owner,
|
|
2382
2398
|
"owner"
|
|
2383
2399
|
);
|
|
2384
2400
|
runtimeUnit(this.owner);
|
|
@@ -2386,17 +2402,17 @@ var init_records = __esm({
|
|
|
2386
2402
|
static create() {
|
|
2387
2403
|
const root = stateDirectory();
|
|
2388
2404
|
mkdirSync9(root, { recursive: true, mode: 448 });
|
|
2389
|
-
const directory =
|
|
2390
|
-
if (Buffer.byteLength(
|
|
2405
|
+
const directory = join29(root, randomUUID6().slice(0, 16));
|
|
2406
|
+
if (Buffer.byteLength(join29(directory, "s")) > 100)
|
|
2391
2407
|
throw new Error("socket_path_too_long");
|
|
2392
2408
|
mkdirSync9(directory, { mode: 448 });
|
|
2393
|
-
mkdirSync9(
|
|
2394
|
-
privateJson(
|
|
2409
|
+
mkdirSync9(join29(directory, "sessions"), { mode: 448 });
|
|
2410
|
+
privateJson(join29(directory, "harness.json"), {
|
|
2395
2411
|
owner: randomUUID6(),
|
|
2396
2412
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2397
2413
|
});
|
|
2398
2414
|
writeFileSync10(
|
|
2399
|
-
|
|
2415
|
+
join29(directory, "tmux.conf"),
|
|
2400
2416
|
[
|
|
2401
2417
|
"set -g remain-on-exit on",
|
|
2402
2418
|
"set -g history-limit 2000",
|
|
@@ -2412,10 +2428,10 @@ var init_records = __esm({
|
|
|
2412
2428
|
}
|
|
2413
2429
|
path(id2) {
|
|
2414
2430
|
runtimeUnit(id2);
|
|
2415
|
-
return
|
|
2431
|
+
return join29(this.directory, "sessions", `${id2}.json`);
|
|
2416
2432
|
}
|
|
2417
2433
|
read(id2) {
|
|
2418
|
-
const record =
|
|
2434
|
+
const record = object8(readPrivateJson2(this.path(id2)));
|
|
2419
2435
|
if (record.id !== id2 || record.owner !== this.owner || record.socket !== this.socket || record.unit !== runtimeUnit(id2))
|
|
2420
2436
|
throw new Error("foreign_backend_record");
|
|
2421
2437
|
for (const [field, pattern] of [
|
|
@@ -2434,7 +2450,7 @@ var init_records = __esm({
|
|
|
2434
2450
|
return record;
|
|
2435
2451
|
}
|
|
2436
2452
|
all() {
|
|
2437
|
-
return
|
|
2453
|
+
return readdirSync10(join29(this.directory, "sessions")).filter((name) => name.endsWith(".json")).map((name) => this.read(name.slice(0, -5)));
|
|
2438
2454
|
}
|
|
2439
2455
|
save(record) {
|
|
2440
2456
|
privateJson(this.path(record.id), record);
|
|
@@ -2515,17 +2531,17 @@ var init_launch_identity = __esm({
|
|
|
2515
2531
|
});
|
|
2516
2532
|
|
|
2517
2533
|
// src/harness/launch/journal.ts
|
|
2518
|
-
import { createHash as
|
|
2519
|
-
import { existsSync as
|
|
2520
|
-
import { join as
|
|
2534
|
+
import { createHash as createHash16 } from "crypto";
|
|
2535
|
+
import { existsSync as existsSync23, readdirSync as readdirSync11 } from "fs";
|
|
2536
|
+
import { join as join30 } from "path";
|
|
2521
2537
|
function launchJournals(records2) {
|
|
2522
|
-
return
|
|
2538
|
+
return readdirSync11(records2.directory).filter((name) => name.endsWith(".intent.json")).map((name) => new LaunchJournal(records2, name.slice(0, -12)));
|
|
2523
2539
|
}
|
|
2524
2540
|
function pendingLaunches(records2) {
|
|
2525
2541
|
return launchJournals(records2).filter((journal) => {
|
|
2526
2542
|
const aborted = journal.abortRecord();
|
|
2527
2543
|
if (aborted) return aborted.state !== "stopped";
|
|
2528
|
-
if (!
|
|
2544
|
+
if (!existsSync23(journal.path("committed"))) return true;
|
|
2529
2545
|
journal.assertCommitted();
|
|
2530
2546
|
return false;
|
|
2531
2547
|
});
|
|
@@ -2552,10 +2568,10 @@ var init_journal = __esm({
|
|
|
2552
2568
|
path(kind2) {
|
|
2553
2569
|
if (!["intent", "proof", "grant", "committed", "aborted"].includes(kind2))
|
|
2554
2570
|
throw new Error("invalid_launch_artifact");
|
|
2555
|
-
return
|
|
2571
|
+
return join30(this.records.directory, `${this.id}.${kind2}.json`);
|
|
2556
2572
|
}
|
|
2557
2573
|
intent() {
|
|
2558
|
-
const value =
|
|
2574
|
+
const value = object8(readPrivateJson2(this.path("intent")));
|
|
2559
2575
|
if (value.id !== this.id || value.owner !== this.records.owner || value.socket !== this.records.socket || value.unit !== runtimeUnit(this.id) || value.slice !== budgetSlice(this.records.owner))
|
|
2560
2576
|
throw new Error("foreign_launch_intent");
|
|
2561
2577
|
if (!/^\d+:\d+$/.test(text(value.controllerIdentity, "controller")))
|
|
@@ -2572,28 +2588,28 @@ var init_journal = __esm({
|
|
|
2572
2588
|
if (value.resource !== void 0)
|
|
2573
2589
|
resourceClaim(value.resource, this.records.owner);
|
|
2574
2590
|
if (value.dockerCreation !== void 0) {
|
|
2575
|
-
const creation =
|
|
2576
|
-
const config =
|
|
2577
|
-
const labels =
|
|
2591
|
+
const creation = object8(value.dockerCreation);
|
|
2592
|
+
const config = object8(creation.configuration);
|
|
2593
|
+
const labels = object8(config.Labels);
|
|
2578
2594
|
if (creation.egress !== void 0) egressPolicy(creation.egress);
|
|
2579
2595
|
const bridge = creation.egress === void 0 ? void 0 : egressPolicy(creation.egress).bridgeExecutable;
|
|
2580
2596
|
if (bridge && !/^[a-f0-9]{64}$/.test(String(creation.bridgeSha256)))
|
|
2581
2597
|
throw new Error("invalid_docker_bridge_digest");
|
|
2582
2598
|
if (!bridge && creation.bridgeSha256 !== void 0)
|
|
2583
2599
|
throw new Error("ungranted_docker_bridge_digest");
|
|
2584
|
-
const policy = new GroupStore(
|
|
2600
|
+
const policy = new GroupStore(join30(this.records.directory, "group.json")).state.group.dockerPolicy;
|
|
2585
2601
|
const profile = selectedProfile(
|
|
2586
2602
|
policy,
|
|
2587
2603
|
creation.privateProfile,
|
|
2588
2604
|
text(value.runtime, "runtime")
|
|
2589
2605
|
);
|
|
2590
|
-
const expectedState = profile?.directory ??
|
|
2606
|
+
const expectedState = profile?.directory ?? join30(
|
|
2591
2607
|
this.records.directory,
|
|
2592
2608
|
"containers",
|
|
2593
2609
|
text(value.nonce, "nonce"),
|
|
2594
2610
|
"profile"
|
|
2595
2611
|
);
|
|
2596
|
-
if (value.resource !== void 0 &&
|
|
2612
|
+
if (value.resource !== void 0 && object8(value.resource).privateProfile !== creation.privateProfile)
|
|
2597
2613
|
throw new Error("launch_private_profile_mismatch");
|
|
2598
2614
|
if (creation.name !== containerName(this.records.owner, this.id) || creation.nonce !== value.nonce || creation.state !== expectedState || labels[containerLabels.owner] !== this.records.owner || labels[containerLabels.role] !== value.role || labels[containerLabels.nonce] !== value.nonce)
|
|
2599
2615
|
throw new Error("foreign_container_creation");
|
|
@@ -2602,8 +2618,8 @@ var init_journal = __esm({
|
|
|
2602
2618
|
}
|
|
2603
2619
|
proof() {
|
|
2604
2620
|
const intent = this.intent();
|
|
2605
|
-
const value =
|
|
2606
|
-
const session =
|
|
2621
|
+
const value = object8(readPrivateJson2(this.path("proof")));
|
|
2622
|
+
const session = object8(value.session);
|
|
2607
2623
|
if (value.nonce !== intent.nonce || session.id !== intent.id || session.owner !== intent.owner || session.socket !== intent.socket || session.unit !== intent.unit || session.cwd !== intent.cwd || session.memoryMb !== intent.memoryMb || session.runtime !== intent.runtime || session.createdAt !== intent.createdAt || JSON.stringify(session.resource) !== JSON.stringify(intent.resource))
|
|
2608
2624
|
throw new Error("foreign_launch_proof");
|
|
2609
2625
|
for (const [field, pattern] of [
|
|
@@ -2628,12 +2644,12 @@ var init_journal = __esm({
|
|
|
2628
2644
|
groupId: intent.groupId,
|
|
2629
2645
|
generation: intent.generation,
|
|
2630
2646
|
nonce: intent.nonce,
|
|
2631
|
-
proofDigest:
|
|
2647
|
+
proofDigest: createHash16("sha256").update(JSON.stringify(this.proof())).digest("hex")
|
|
2632
2648
|
};
|
|
2633
2649
|
}
|
|
2634
2650
|
assertRegistered() {
|
|
2635
2651
|
const intent = this.intent();
|
|
2636
|
-
const group = new GroupStore(
|
|
2652
|
+
const group = new GroupStore(join30(this.records.directory, "group.json")).state.group;
|
|
2637
2653
|
if (group.id !== intent.groupId || group.generation !== intent.generation)
|
|
2638
2654
|
throw new Error("launch_group_generation_mismatch");
|
|
2639
2655
|
if (intent.dockerCreation && JSON.stringify(intent.dockerCreation.egress) !== JSON.stringify(group.dockerPolicy?.egress))
|
|
@@ -2649,13 +2665,13 @@ var init_journal = __esm({
|
|
|
2649
2665
|
throw new Error("launch_record_mismatch");
|
|
2650
2666
|
}
|
|
2651
2667
|
authorize() {
|
|
2652
|
-
if (
|
|
2668
|
+
if (existsSync23(this.path("aborted"))) throw new Error("launch_was_aborted");
|
|
2653
2669
|
this.assertRegistered();
|
|
2654
2670
|
privateJson(this.path("grant"), this.grant());
|
|
2655
2671
|
}
|
|
2656
2672
|
assertGrant() {
|
|
2657
|
-
if (
|
|
2658
|
-
const grant =
|
|
2673
|
+
if (existsSync23(this.path("aborted"))) throw new Error("launch_was_aborted");
|
|
2674
|
+
const grant = object8(readPrivateJson2(this.path("grant")));
|
|
2659
2675
|
if (JSON.stringify(grant) !== JSON.stringify(this.grant()))
|
|
2660
2676
|
throw new Error("launch_grant_mismatch");
|
|
2661
2677
|
}
|
|
@@ -2666,13 +2682,13 @@ var init_journal = __esm({
|
|
|
2666
2682
|
}
|
|
2667
2683
|
assertCommitted() {
|
|
2668
2684
|
this.assertGrant();
|
|
2669
|
-
const committed =
|
|
2685
|
+
const committed = object8(readPrivateJson2(this.path("committed")));
|
|
2670
2686
|
if (JSON.stringify(committed) !== JSON.stringify(this.grant()))
|
|
2671
2687
|
throw new Error("launch_commit_mismatch");
|
|
2672
2688
|
}
|
|
2673
2689
|
abortRecord() {
|
|
2674
|
-
if (!
|
|
2675
|
-
const value =
|
|
2690
|
+
if (!existsSync23(this.path("aborted"))) return;
|
|
2691
|
+
const value = object8(readPrivateJson2(this.path("aborted")));
|
|
2676
2692
|
if (value.id !== this.id || value.owner !== this.records.owner || !["stopping", "stopped"].includes(String(value.state)))
|
|
2677
2693
|
throw new Error("foreign_launch_abort");
|
|
2678
2694
|
return value;
|
|
@@ -2690,10 +2706,10 @@ function stopResource(resource) {
|
|
|
2690
2706
|
if (resourceAbsent(error, resource.id)) return;
|
|
2691
2707
|
throw error;
|
|
2692
2708
|
}
|
|
2693
|
-
const state =
|
|
2709
|
+
const state = object8(actual.State);
|
|
2694
2710
|
if (state.Running === true)
|
|
2695
2711
|
command("docker", ["stop", "--timeout", "2", resource.id]);
|
|
2696
|
-
if (
|
|
2712
|
+
if (object8(inspectResource(resource, false).State).Running !== false)
|
|
2697
2713
|
throw new Error("container_stop_unconfirmed");
|
|
2698
2714
|
if (resource.managed) {
|
|
2699
2715
|
command("docker", ["rm", resource.id]);
|
|
@@ -2720,28 +2736,28 @@ import { homedir as homedir8 } from "os";
|
|
|
2720
2736
|
import {
|
|
2721
2737
|
closeSync as closeSync4,
|
|
2722
2738
|
constants as constants4,
|
|
2723
|
-
existsSync as
|
|
2739
|
+
existsSync as existsSync24,
|
|
2724
2740
|
fstatSync as fstatSync3,
|
|
2725
|
-
lstatSync as
|
|
2741
|
+
lstatSync as lstatSync6,
|
|
2726
2742
|
openSync as openSync4,
|
|
2727
|
-
realpathSync as
|
|
2743
|
+
realpathSync as realpathSync6,
|
|
2728
2744
|
statfsSync
|
|
2729
2745
|
} from "fs";
|
|
2730
|
-
import { join as
|
|
2746
|
+
import { join as join31, relative as relative11 } from "path";
|
|
2731
2747
|
function overlap(left, right) {
|
|
2732
|
-
const scope =
|
|
2748
|
+
const scope = relative11(left, right);
|
|
2733
2749
|
return scope === "" || scope !== ".." && !scope.startsWith("../");
|
|
2734
2750
|
}
|
|
2735
2751
|
function profileIdentity(directory, projectRoot) {
|
|
2736
|
-
if (directory === "/" || directory ===
|
|
2752
|
+
if (directory === "/" || directory === realpathSync6(homedir8()) || overlap(directory, projectRoot) || overlap(projectRoot, directory) || realpathSync6(directory) !== directory)
|
|
2737
2753
|
throw new Error("private_profile_scope_not_admitted");
|
|
2738
|
-
const stat =
|
|
2754
|
+
const stat = lstatSync6(directory);
|
|
2739
2755
|
if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 511) !== 448)
|
|
2740
2756
|
throw new Error("unsafe_private_profile_directory");
|
|
2741
2757
|
if ([26985, 4283649346, 4266872130].includes(statfsSync(directory).type))
|
|
2742
2758
|
throw new Error("remote_private_profile_not_admitted");
|
|
2743
2759
|
const lock = openSync4(
|
|
2744
|
-
|
|
2760
|
+
join31(directory, privateProfileLock),
|
|
2745
2761
|
constants4.O_RDONLY | constants4.O_NOFOLLOW
|
|
2746
2762
|
);
|
|
2747
2763
|
try {
|
|
@@ -2754,14 +2770,14 @@ function profileIdentity(directory, projectRoot) {
|
|
|
2754
2770
|
}
|
|
2755
2771
|
}
|
|
2756
2772
|
function captureProfile(grant, projectRoot) {
|
|
2757
|
-
const directory =
|
|
2758
|
-
if (directory !== grant.directory || overlap(directory, projectRoot) || overlap(projectRoot, directory) || directory === "/" || directory ===
|
|
2773
|
+
const directory = realpathSync6(grant.directory);
|
|
2774
|
+
if (directory !== grant.directory || overlap(directory, projectRoot) || overlap(projectRoot, directory) || directory === "/" || directory === realpathSync6(homedir8()))
|
|
2759
2775
|
throw new Error("private_profile_scope_not_admitted");
|
|
2760
|
-
const stat =
|
|
2776
|
+
const stat = lstatSync6(directory);
|
|
2761
2777
|
if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 511) !== 448)
|
|
2762
2778
|
throw new Error("unsafe_private_profile_directory");
|
|
2763
|
-
const lock =
|
|
2764
|
-
if (!
|
|
2779
|
+
const lock = join31(directory, privateProfileLock);
|
|
2780
|
+
if (!existsSync24(lock)) closeSync4(openSync4(lock, "wx", 384));
|
|
2765
2781
|
const identity = profileIdentity(directory, projectRoot);
|
|
2766
2782
|
if (grant.identity && grant.identity !== identity)
|
|
2767
2783
|
throw new Error("private_profile_identity_changed");
|
|
@@ -2779,7 +2795,7 @@ function assertProfileAvailable(grant, projectRoot) {
|
|
|
2779
2795
|
"--nonblock",
|
|
2780
2796
|
"--conflict-exit-code",
|
|
2781
2797
|
"73",
|
|
2782
|
-
|
|
2798
|
+
join31(grant.directory, privateProfileLock),
|
|
2783
2799
|
"true"
|
|
2784
2800
|
]);
|
|
2785
2801
|
} catch (error) {
|
|
@@ -2799,8 +2815,8 @@ var init_private_profile = __esm({
|
|
|
2799
2815
|
|
|
2800
2816
|
// src/harness/adapters/docker/profile.ts
|
|
2801
2817
|
import { homedir as homedir9 } from "os";
|
|
2802
|
-
import { lstatSync as
|
|
2803
|
-
import { relative as
|
|
2818
|
+
import { lstatSync as lstatSync7, realpathSync as realpathSync7 } from "fs";
|
|
2819
|
+
import { relative as relative12 } from "path";
|
|
2804
2820
|
function assertLocalDocker() {
|
|
2805
2821
|
if (process.env.DOCKER_HOST)
|
|
2806
2822
|
throw new Error("explicit_docker_host_not_admitted");
|
|
@@ -2816,13 +2832,13 @@ function assertLocalDocker() {
|
|
|
2816
2832
|
throw new Error("remote_docker_context_not_admitted");
|
|
2817
2833
|
}
|
|
2818
2834
|
function loadDockerPolicy(path, projectRoot, runtimes) {
|
|
2819
|
-
if (projectRoot === "/" || projectRoot ===
|
|
2835
|
+
if (projectRoot === "/" || projectRoot === realpathSync7(homedir9()))
|
|
2820
2836
|
throw new Error("docker_project_root_too_broad");
|
|
2821
2837
|
const inputPath = text(path, "docker_policy_file");
|
|
2822
|
-
if (
|
|
2838
|
+
if (lstatSync7(inputPath).isSymbolicLink())
|
|
2823
2839
|
throw new Error("symlink_docker_policy_not_admitted");
|
|
2824
|
-
const source2 =
|
|
2825
|
-
const scope =
|
|
2840
|
+
const source2 = realpathSync7(inputPath);
|
|
2841
|
+
const scope = relative12(projectRoot, source2);
|
|
2826
2842
|
if (scope === ".." || scope.startsWith("../"))
|
|
2827
2843
|
throw new Error("docker_policy_outside_project");
|
|
2828
2844
|
const policy = dockerPolicy(readPrivateJson2(source2));
|
|
@@ -2855,21 +2871,21 @@ var init_profile = __esm({
|
|
|
2855
2871
|
});
|
|
2856
2872
|
|
|
2857
2873
|
// src/harness/adapters/docker/mounts.ts
|
|
2858
|
-
import { lstatSync as
|
|
2859
|
-
import { join as
|
|
2874
|
+
import { lstatSync as lstatSync8, realpathSync as realpathSync8 } from "fs";
|
|
2875
|
+
import { join as join32 } from "path";
|
|
2860
2876
|
function assertContainerMounts(actual, statePath, projectRoot, directory, nonce, policy, privateProfile, runtime2) {
|
|
2861
2877
|
const state = text(statePath, "container_state_directory");
|
|
2862
2878
|
const profile = selectedProfile(policy, privateProfile, runtime2 ?? "");
|
|
2863
2879
|
if (profile) assertProfileIdentity(profile, projectRoot);
|
|
2864
|
-
const expected = profile?.directory ??
|
|
2865
|
-
if (state !== expected ||
|
|
2880
|
+
const expected = profile?.directory ?? join32(directory, "containers", nonce, "profile");
|
|
2881
|
+
if (state !== expected || realpathSync8(state) !== expected)
|
|
2866
2882
|
throw new Error("foreign_container_state_directory");
|
|
2867
|
-
const stat =
|
|
2883
|
+
const stat = lstatSync8(state);
|
|
2868
2884
|
if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0)
|
|
2869
2885
|
throw new Error("unsafe_container_state_directory");
|
|
2870
2886
|
if (!Array.isArray(actual.Mounts) || actual.Mounts.length !== 2)
|
|
2871
2887
|
throw new Error("ungranted_container_mount");
|
|
2872
|
-
if (new Set(actual.Mounts.map((value) =>
|
|
2888
|
+
if (new Set(actual.Mounts.map((value) => object8(value).Destination)).size !== 2)
|
|
2873
2889
|
throw new Error("ungranted_container_mount");
|
|
2874
2890
|
const expectedMounts = [
|
|
2875
2891
|
{
|
|
@@ -2880,15 +2896,15 @@ function assertContainerMounts(actual, statePath, projectRoot, directory, nonce,
|
|
|
2880
2896
|
{ Source: state, Destination: "/state", RW: true }
|
|
2881
2897
|
];
|
|
2882
2898
|
for (const value of actual.Mounts) {
|
|
2883
|
-
const mount =
|
|
2899
|
+
const mount = object8(value);
|
|
2884
2900
|
if (mount.Type !== "bind" || mount.Propagation !== "rprivate" || !expectedMounts.some(
|
|
2885
2901
|
(expected2) => mount.Source === expected2.Source && mount.Destination === expected2.Destination && mount.RW === expected2.RW
|
|
2886
2902
|
))
|
|
2887
2903
|
throw new Error("ungranted_container_mount");
|
|
2888
2904
|
}
|
|
2889
|
-
const host =
|
|
2905
|
+
const host = object8(actual.HostConfig);
|
|
2890
2906
|
if (!Array.isArray(host.Mounts) || host.Mounts.length !== 2 || host.Mounts.some(
|
|
2891
|
-
(value) =>
|
|
2907
|
+
(value) => object8(object8(value).BindOptions).NonRecursive !== true
|
|
2892
2908
|
))
|
|
2893
2909
|
throw new Error("recursive_container_mount_not_admitted");
|
|
2894
2910
|
}
|
|
@@ -2905,9 +2921,9 @@ var init_mounts = __esm({
|
|
|
2905
2921
|
function assertContainerIsolation(host, network) {
|
|
2906
2922
|
if (host.ReadonlyRootfs !== true || host.Privileged !== false || host.NetworkMode !== network || host.CgroupnsMode !== "private")
|
|
2907
2923
|
throw new Error("invalid_container_isolation_policy");
|
|
2908
|
-
if (JSON.stringify(host.CapDrop) !== '["ALL"]' || JSON.stringify(host.SecurityOpt) !== '["no-new-privileges"]' ||
|
|
2924
|
+
if (JSON.stringify(host.CapDrop) !== '["ALL"]' || JSON.stringify(host.SecurityOpt) !== '["no-new-privileges"]' || object8(host.RestartPolicy).Name !== "no")
|
|
2909
2925
|
throw new Error("invalid_container_execution_policy");
|
|
2910
|
-
if (host.CapAdd !== null || host.PidMode !== "" || host.UTSMode !== "" || host.UsernsMode !== "" || host.CgroupParent !== "" || host.IpcMode !== "private" || hasDevices(host.Devices) || hasDevices(host.DeviceRequests) || host.AutoRemove !== false || host.VolumesFrom !== null || host.GroupAdd !== null || host.Binds !== null || Object.keys(
|
|
2926
|
+
if (host.CapAdd !== null || host.PidMode !== "" || host.UTSMode !== "" || host.UsernsMode !== "" || host.CgroupParent !== "" || host.IpcMode !== "private" || hasDevices(host.Devices) || hasDevices(host.DeviceRequests) || host.AutoRemove !== false || host.VolumesFrom !== null || host.GroupAdd !== null || host.Binds !== null || Object.keys(object8(host.PortBindings)).length)
|
|
2911
2927
|
throw new Error("ungranted_container_host_authority");
|
|
2912
2928
|
}
|
|
2913
2929
|
function hasDevices(value) {
|
|
@@ -2956,21 +2972,21 @@ var init_entrypoint = __esm({
|
|
|
2956
2972
|
});
|
|
2957
2973
|
|
|
2958
2974
|
// src/harness/adapters/docker/admission.ts
|
|
2959
|
-
import { lstatSync as
|
|
2960
|
-
import { join as
|
|
2975
|
+
import { lstatSync as lstatSync9, realpathSync as realpathSync9 } from "fs";
|
|
2976
|
+
import { join as join33, relative as relative13 } from "path";
|
|
2961
2977
|
function admitResource(path, group, role, directory, memoryMb, runtime2) {
|
|
2962
2978
|
if (group.profile !== "docker" || !group.dockerPolicy)
|
|
2963
2979
|
throw new Error("container_requires_docker_profile");
|
|
2964
2980
|
const { projectRoot } = group;
|
|
2965
2981
|
assertLocalDocker();
|
|
2966
2982
|
const inputPath = text(path, "container_file");
|
|
2967
|
-
if (
|
|
2983
|
+
if (lstatSync9(inputPath).isSymbolicLink())
|
|
2968
2984
|
throw new Error("symlink_container_file_not_admitted");
|
|
2969
|
-
const source2 =
|
|
2970
|
-
const scope =
|
|
2985
|
+
const source2 = realpathSync9(inputPath);
|
|
2986
|
+
const scope = relative13(projectRoot, source2);
|
|
2971
2987
|
if (scope === ".." || scope.startsWith("../"))
|
|
2972
2988
|
throw new Error("container_file_outside_project");
|
|
2973
|
-
const claim =
|
|
2989
|
+
const claim = object8(readPrivateJson2(source2));
|
|
2974
2990
|
if (claim.privateProfile !== void 0)
|
|
2975
2991
|
throw new Error("private_profile_requires_cli_owned_creation");
|
|
2976
2992
|
return admitClaim(claim, group, role, directory, memoryMb, runtime2);
|
|
@@ -2981,7 +2997,7 @@ function admitClaim(value, group, role, directory, memoryMb, runtime2, cwd = gro
|
|
|
2981
2997
|
assertLocalDocker();
|
|
2982
2998
|
const policy = group.dockerPolicy;
|
|
2983
2999
|
const { owner, projectRoot } = group;
|
|
2984
|
-
const claim =
|
|
3000
|
+
const claim = object8(value);
|
|
2985
3001
|
if (claim.owner !== owner || claim.role !== role || claim.runtime !== runtime2)
|
|
2986
3002
|
throw new Error("foreign_container_resource");
|
|
2987
3003
|
const resource = {
|
|
@@ -3001,9 +3017,9 @@ function admitClaim(value, group, role, directory, memoryMb, runtime2, cwd = gro
|
|
|
3001
3017
|
))
|
|
3002
3018
|
throw new Error("invalid_container_resource");
|
|
3003
3019
|
const actual = inspectResource(resource, false);
|
|
3004
|
-
const state =
|
|
3005
|
-
const config =
|
|
3006
|
-
const host =
|
|
3020
|
+
const state = object8(actual.State);
|
|
3021
|
+
const config = object8(actual.Config);
|
|
3022
|
+
const host = object8(actual.HostConfig);
|
|
3007
3023
|
if (resource.image !== policy.image || JSON.stringify(config.Entrypoint) !== JSON.stringify(
|
|
3008
3024
|
containerEntrypoint(
|
|
3009
3025
|
policy,
|
|
@@ -3013,7 +3029,7 @@ function admitClaim(value, group, role, directory, memoryMb, runtime2, cwd = gro
|
|
|
3013
3029
|
)
|
|
3014
3030
|
))
|
|
3015
3031
|
throw new Error("container_runtime_image_not_granted");
|
|
3016
|
-
if (config.WorkingDir !==
|
|
3032
|
+
if (config.WorkingDir !== join33("/workspace", relative13(projectRoot, cwd)))
|
|
3017
3033
|
throw new Error("container_cwd_not_granted");
|
|
3018
3034
|
assertContainerMounts(
|
|
3019
3035
|
actual,
|
|
@@ -3092,7 +3108,7 @@ var init_engine = __esm({
|
|
|
3092
3108
|
if (!/^1\.\d+$/.test(version2))
|
|
3093
3109
|
throw new Error("invalid_docker_api_version");
|
|
3094
3110
|
const payload = JSON.stringify(configuration);
|
|
3095
|
-
return new Promise((
|
|
3111
|
+
return new Promise((resolve22, reject) => {
|
|
3096
3112
|
const req = request2(
|
|
3097
3113
|
{
|
|
3098
3114
|
socketPath: new URL(endpoint).pathname,
|
|
@@ -3122,12 +3138,12 @@ var init_engine = __esm({
|
|
|
3122
3138
|
}
|
|
3123
3139
|
try {
|
|
3124
3140
|
const id2 = text(
|
|
3125
|
-
|
|
3141
|
+
object8(JSON.parse(Buffer.concat(chunks).toString("utf8"))).Id,
|
|
3126
3142
|
"container_id"
|
|
3127
3143
|
);
|
|
3128
3144
|
if (!/^[a-f0-9]{64}$/.test(id2))
|
|
3129
3145
|
throw new Error("invalid_container_id");
|
|
3130
|
-
|
|
3146
|
+
resolve22(id2);
|
|
3131
3147
|
} catch (error) {
|
|
3132
3148
|
reject(error);
|
|
3133
3149
|
}
|
|
@@ -3147,25 +3163,25 @@ var init_engine = __esm({
|
|
|
3147
3163
|
});
|
|
3148
3164
|
|
|
3149
3165
|
// src/harness/adapters/docker/bridge-artifact.ts
|
|
3150
|
-
import { createHash as
|
|
3166
|
+
import { createHash as createHash17 } from "crypto";
|
|
3151
3167
|
import {
|
|
3152
3168
|
closeSync as closeSync5,
|
|
3153
3169
|
constants as constants5,
|
|
3154
3170
|
fstatSync as fstatSync4,
|
|
3155
3171
|
fsyncSync as fsyncSync2,
|
|
3156
3172
|
openSync as openSync5,
|
|
3157
|
-
readFileSync as
|
|
3173
|
+
readFileSync as readFileSync35,
|
|
3158
3174
|
writeFileSync as writeFileSync11
|
|
3159
3175
|
} from "fs";
|
|
3160
|
-
import { join as
|
|
3176
|
+
import { join as join34 } from "path";
|
|
3161
3177
|
function source() {
|
|
3162
3178
|
const path = artifact("egress-bridge");
|
|
3163
3179
|
if (!path.endsWith(".js"))
|
|
3164
3180
|
throw new Error("docker_bridge_requires_compiled_cli");
|
|
3165
|
-
return
|
|
3181
|
+
return readFileSync35(path);
|
|
3166
3182
|
}
|
|
3167
3183
|
function digest(value) {
|
|
3168
|
-
return
|
|
3184
|
+
return createHash17("sha256").update(value).digest("hex");
|
|
3169
3185
|
}
|
|
3170
3186
|
function bridgeDigest() {
|
|
3171
3187
|
return digest(source());
|
|
@@ -3176,7 +3192,7 @@ function prepareBridge(creation) {
|
|
|
3176
3192
|
if (digest(content) !== creation.bridgeSha256)
|
|
3177
3193
|
throw new Error("docker_bridge_source_changed");
|
|
3178
3194
|
const descriptor = openSync5(
|
|
3179
|
-
|
|
3195
|
+
join34(creation.state, bridgeName(creation.nonce)),
|
|
3180
3196
|
"wx",
|
|
3181
3197
|
384
|
|
3182
3198
|
);
|
|
@@ -3190,12 +3206,12 @@ function prepareBridge(creation) {
|
|
|
3190
3206
|
function assertBridge(creation) {
|
|
3191
3207
|
if (!creation.egress?.bridgeExecutable) return;
|
|
3192
3208
|
const descriptor = openSync5(
|
|
3193
|
-
|
|
3209
|
+
join34(creation.state, bridgeName(creation.nonce)),
|
|
3194
3210
|
constants5.O_RDONLY | constants5.O_NOFOLLOW
|
|
3195
3211
|
);
|
|
3196
3212
|
try {
|
|
3197
3213
|
const stat = fstatSync4(descriptor);
|
|
3198
|
-
if (!stat.isFile() || stat.uid !== process.getuid?.() || stat.nlink !== 1 || (stat.mode & 63) !== 0 || stat.size > 2097152 || digest(
|
|
3214
|
+
if (!stat.isFile() || stat.uid !== process.getuid?.() || stat.nlink !== 1 || (stat.mode & 63) !== 0 || stat.size > 2097152 || digest(readFileSync35(descriptor)) !== creation.bridgeSha256)
|
|
3199
3215
|
throw new Error("docker_bridge_artifact_changed");
|
|
3200
3216
|
} finally {
|
|
3201
3217
|
closeSync5(descriptor);
|
|
@@ -3210,7 +3226,7 @@ var init_bridge_artifact = __esm({
|
|
|
3210
3226
|
});
|
|
3211
3227
|
|
|
3212
3228
|
// src/harness/adapters/docker/creation-plan.ts
|
|
3213
|
-
import { join as
|
|
3229
|
+
import { join as join35, relative as relative14 } from "path";
|
|
3214
3230
|
function creationPlan(group, directory, id2, nonce, role, runtime2, cwd, preparation) {
|
|
3215
3231
|
if (group.profile !== "docker" || !group.dockerPolicy)
|
|
3216
3232
|
throw new Error("container_requires_docker_profile");
|
|
@@ -3219,15 +3235,15 @@ function creationPlan(group, directory, id2, nonce, role, runtime2, cwd, prepara
|
|
|
3219
3235
|
const images = JSON.parse(
|
|
3220
3236
|
command("docker", ["image", "inspect", policy.image])
|
|
3221
3237
|
);
|
|
3222
|
-
if (!Array.isArray(images) || images.length !== 1 ||
|
|
3238
|
+
if (!Array.isArray(images) || images.length !== 1 || object8(images[0]).Id !== policy.image)
|
|
3223
3239
|
throw new Error("docker_image_not_available");
|
|
3224
|
-
const imageConfig =
|
|
3240
|
+
const imageConfig = object8(object8(images[0]).Config);
|
|
3225
3241
|
const environment2 = imageConfig.Env;
|
|
3226
3242
|
if (!Array.isArray(environment2) || environment2.some((entry2) => typeof entry2 !== "string"))
|
|
3227
3243
|
throw new Error("invalid_container_image_environment");
|
|
3228
3244
|
const profile = selectedProfile(policy, preparation.privateProfile, runtime2);
|
|
3229
3245
|
if (profile) assertProfileAvailable(profile, group.projectRoot);
|
|
3230
|
-
const state = profile?.directory ??
|
|
3246
|
+
const state = profile?.directory ?? join35(directory, "containers", nonce, "profile");
|
|
3231
3247
|
return {
|
|
3232
3248
|
name: containerName(group.owner, id2),
|
|
3233
3249
|
nonce,
|
|
@@ -3244,7 +3260,7 @@ function creationPlan(group, directory, id2, nonce, role, runtime2, cwd, prepara
|
|
|
3244
3260
|
preparation.privateProfile
|
|
3245
3261
|
),
|
|
3246
3262
|
Cmd: preparation.args.length ? preparation.args : null,
|
|
3247
|
-
WorkingDir:
|
|
3263
|
+
WorkingDir: join35("/workspace", relative14(group.projectRoot, cwd)),
|
|
3248
3264
|
User: `${process.getuid?.()}:${process.getgid?.()}`,
|
|
3249
3265
|
Env: [
|
|
3250
3266
|
...environment2.filter(
|
|
@@ -3303,14 +3319,14 @@ function assertCreationConfiguration(actual, creation) {
|
|
|
3303
3319
|
if (actual.Name !== `/${creation.name}`)
|
|
3304
3320
|
throw new Error("foreign_container_creation");
|
|
3305
3321
|
const expected = creation.configuration;
|
|
3306
|
-
const config =
|
|
3307
|
-
const host =
|
|
3322
|
+
const config = object8(actual.Config);
|
|
3323
|
+
const host = object8(actual.HostConfig);
|
|
3308
3324
|
for (const [key, value] of Object.entries(expected)) {
|
|
3309
3325
|
if (key === "HostConfig" || key === "Image") continue;
|
|
3310
3326
|
if (!matchesFields(config[key], value))
|
|
3311
3327
|
throw new Error(`container_creation_configuration_changed_${key}`);
|
|
3312
3328
|
}
|
|
3313
|
-
for (const [key, value] of Object.entries(
|
|
3329
|
+
for (const [key, value] of Object.entries(object8(expected.HostConfig))) {
|
|
3314
3330
|
if (!matchesFields(host[key], value))
|
|
3315
3331
|
throw new Error(`container_creation_configuration_changed_${key}`);
|
|
3316
3332
|
}
|
|
@@ -3343,12 +3359,12 @@ var init_creation_plan = __esm({
|
|
|
3343
3359
|
|
|
3344
3360
|
// src/harness/adapters/docker/creation.ts
|
|
3345
3361
|
import { mkdirSync as mkdirSync10 } from "fs";
|
|
3346
|
-
import { join as
|
|
3362
|
+
import { join as join36 } from "path";
|
|
3347
3363
|
async function prepareCreation(journal) {
|
|
3348
3364
|
const intent = journal.intent();
|
|
3349
3365
|
const creation = intent.dockerCreation;
|
|
3350
3366
|
if (!creation) throw new Error("container_creation_missing");
|
|
3351
|
-
const group = new GroupStore(
|
|
3367
|
+
const group = new GroupStore(join36(journal.records.directory, "group.json")).state.group;
|
|
3352
3368
|
const profile = selectedProfile(
|
|
3353
3369
|
group.dockerPolicy,
|
|
3354
3370
|
creation.privateProfile,
|
|
@@ -3362,7 +3378,7 @@ async function prepareCreation(journal) {
|
|
|
3362
3378
|
creation.name,
|
|
3363
3379
|
creation.configuration
|
|
3364
3380
|
);
|
|
3365
|
-
const current = new GroupStore(
|
|
3381
|
+
const current = new GroupStore(join36(journal.records.directory, "group.json")).state.group;
|
|
3366
3382
|
if (current.id !== intent.groupId || current.generation !== intent.generation)
|
|
3367
3383
|
throw new Error("launch_group_generation_mismatch");
|
|
3368
3384
|
const resource = {
|
|
@@ -3387,7 +3403,7 @@ async function prepareCreation(journal) {
|
|
|
3387
3403
|
managed: true
|
|
3388
3404
|
};
|
|
3389
3405
|
assertCreationConfiguration(
|
|
3390
|
-
|
|
3406
|
+
object8(JSON.parse(command("docker", ["inspect", id2]))[0]),
|
|
3391
3407
|
creation
|
|
3392
3408
|
);
|
|
3393
3409
|
privateJson(journal.path("intent"), {
|
|
@@ -3409,7 +3425,7 @@ function findCreation(journal) {
|
|
|
3409
3425
|
);
|
|
3410
3426
|
if (!Array.isArray(values) || values.length !== 1)
|
|
3411
3427
|
throw new Error("invalid_container_inspection");
|
|
3412
|
-
actual =
|
|
3428
|
+
actual = object8(values[0]);
|
|
3413
3429
|
} catch (error) {
|
|
3414
3430
|
if (resourceAbsent(error, creation.name)) return;
|
|
3415
3431
|
throw error;
|
|
@@ -3422,15 +3438,15 @@ function findCreation(journal) {
|
|
|
3422
3438
|
role: intent.role,
|
|
3423
3439
|
image: text(creation.configuration.Image, "container_image"),
|
|
3424
3440
|
configurationDigest: configurationDigest(actual),
|
|
3425
|
-
memoryMb: Number(
|
|
3426
|
-
cpuQuota: Number(
|
|
3441
|
+
memoryMb: Number(object8(actual.HostConfig).Memory) / 1048576,
|
|
3442
|
+
cpuQuota: Number(object8(actual.HostConfig).CpuQuota),
|
|
3427
3443
|
managed: true,
|
|
3428
3444
|
...creation.privateProfile ? { privateProfile: creation.privateProfile } : {}
|
|
3429
3445
|
};
|
|
3430
3446
|
assertResourceOwner(resource, actual);
|
|
3431
3447
|
assertCreationConfiguration(actual, creation);
|
|
3432
3448
|
if (!intent.resource) {
|
|
3433
|
-
const group = new GroupStore(
|
|
3449
|
+
const group = new GroupStore(join36(journal.records.directory, "group.json")).state.group;
|
|
3434
3450
|
return {
|
|
3435
3451
|
...admitClaim(
|
|
3436
3452
|
{ ...resource, runtime: intent.runtime, state: creation.state },
|
|
@@ -3467,7 +3483,7 @@ var init_creation = __esm({
|
|
|
3467
3483
|
});
|
|
3468
3484
|
|
|
3469
3485
|
// src/harness/launch/recovery.ts
|
|
3470
|
-
import { existsSync as
|
|
3486
|
+
import { existsSync as existsSync25 } from "fs";
|
|
3471
3487
|
function abortLaunch(connection, journal) {
|
|
3472
3488
|
const intent = journal.intent();
|
|
3473
3489
|
if (!controllerExited(intent.controllerIdentity))
|
|
@@ -3484,7 +3500,7 @@ function abortLaunch(connection, journal) {
|
|
|
3484
3500
|
inputReplayed: false
|
|
3485
3501
|
};
|
|
3486
3502
|
}
|
|
3487
|
-
if (!
|
|
3503
|
+
if (!existsSync25(journal.path("proof"))) {
|
|
3488
3504
|
const paneUnknown = connection.alive() && (!intent.dockerCreation || connection.tmux(["list-sessions", "-F", "#{session_name}"]).trim().split("\n").includes(`pmcp-${intent.id}`));
|
|
3489
3505
|
if (paneUnknown || unitActive(intent.unit))
|
|
3490
3506
|
throw new Error("launch_identity_unknown");
|
|
@@ -3511,7 +3527,7 @@ function abortLaunch(connection, journal) {
|
|
|
3511
3527
|
assertProof(connection, proof);
|
|
3512
3528
|
connection.tmux(["kill-pane", "-t", proof.session.paneId]);
|
|
3513
3529
|
}
|
|
3514
|
-
if (
|
|
3530
|
+
if (existsSync25(journal.records.path(intent.id)))
|
|
3515
3531
|
journal.records.save({
|
|
3516
3532
|
...proof.session,
|
|
3517
3533
|
stoppedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -3538,9 +3554,9 @@ var init_recovery = __esm({
|
|
|
3538
3554
|
|
|
3539
3555
|
// src/harness/adapters/tmux/lifecycle.ts
|
|
3540
3556
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
3541
|
-
import { existsSync as
|
|
3557
|
+
import { existsSync as existsSync26 } from "fs";
|
|
3542
3558
|
import { setTimeout as setTimeout2 } from "timers/promises";
|
|
3543
|
-
import { join as
|
|
3559
|
+
import { join as join37 } from "path";
|
|
3544
3560
|
async function start(connection, spec) {
|
|
3545
3561
|
const records2 = connection.records;
|
|
3546
3562
|
if (spec.budgetSlice !== budgetSlice(records2.owner))
|
|
@@ -3554,7 +3570,7 @@ async function start(connection, spec) {
|
|
|
3554
3570
|
const nonce = randomUUID7();
|
|
3555
3571
|
const journal = new LaunchJournal(records2, id2);
|
|
3556
3572
|
const dockerCreation = spec.dockerPreparation ? creationPlan(
|
|
3557
|
-
new GroupStore(
|
|
3573
|
+
new GroupStore(join37(records2.directory, "group.json")).state.group,
|
|
3558
3574
|
records2.directory,
|
|
3559
3575
|
id2,
|
|
3560
3576
|
nonce,
|
|
@@ -3614,7 +3630,7 @@ async function start(connection, spec) {
|
|
|
3614
3630
|
`exec ${argv.map(quote2).join(" ")}`
|
|
3615
3631
|
]);
|
|
3616
3632
|
const deadline = Date.now() + 5e3;
|
|
3617
|
-
while (!
|
|
3633
|
+
while (!existsSync26(journal.path("proof"))) {
|
|
3618
3634
|
if (Date.now() >= deadline) throw new Error("launch_proof_timeout");
|
|
3619
3635
|
await setTimeout2(25);
|
|
3620
3636
|
}
|
|
@@ -3625,7 +3641,7 @@ async function start(connection, spec) {
|
|
|
3625
3641
|
}
|
|
3626
3642
|
function stop(connection, id2) {
|
|
3627
3643
|
const journal = new LaunchJournal(connection.records, id2);
|
|
3628
|
-
if (
|
|
3644
|
+
if (existsSync26(journal.path("intent"))) {
|
|
3629
3645
|
abortLaunch(connection, journal);
|
|
3630
3646
|
return;
|
|
3631
3647
|
}
|
|
@@ -3652,7 +3668,7 @@ function cleanup(connection) {
|
|
|
3652
3668
|
}
|
|
3653
3669
|
for (const record of records2) {
|
|
3654
3670
|
if (record.resource) stopResource(record.resource);
|
|
3655
|
-
if (record.stoppedAt || !
|
|
3671
|
+
if (record.stoppedAt || !existsSync26(
|
|
3656
3672
|
new LaunchJournal(connection.records, record.id).path("intent")
|
|
3657
3673
|
)) {
|
|
3658
3674
|
stopUnit(record.unit);
|
|
@@ -3684,7 +3700,7 @@ var init_lifecycle2 = __esm({
|
|
|
3684
3700
|
// src/harness/adapters/tmux/input.ts
|
|
3685
3701
|
import { randomUUID as randomUUID8 } from "crypto";
|
|
3686
3702
|
import { closeSync as closeSync6, openSync as openSync6, unlinkSync as unlinkSync3, writeFileSync as writeFileSync12 } from "fs";
|
|
3687
|
-
import { join as
|
|
3703
|
+
import { join as join38 } from "path";
|
|
3688
3704
|
function validateText(text3) {
|
|
3689
3705
|
if (typeof text3 !== "string" || Buffer.byteLength(text3) > 16384)
|
|
3690
3706
|
throw new Error("invalid_text");
|
|
@@ -3715,7 +3731,7 @@ function write(connection, id2, text3, submit) {
|
|
|
3715
3731
|
validateText(text3);
|
|
3716
3732
|
const session = connection.inspect(id2);
|
|
3717
3733
|
if (!session.alive) throw new Error("runtime_exited");
|
|
3718
|
-
const lock =
|
|
3734
|
+
const lock = join38(connection.records.directory, `${id2}.input.lock`);
|
|
3719
3735
|
const descriptor = openSync6(lock, "wx", 384);
|
|
3720
3736
|
const buffer = `pmcp-${randomUUID8()}`;
|
|
3721
3737
|
try {
|
|
@@ -3874,11 +3890,11 @@ var init_backend = __esm({
|
|
|
3874
3890
|
});
|
|
3875
3891
|
|
|
3876
3892
|
// src/harness/adapters/systemd/budget.ts
|
|
3877
|
-
import { join as
|
|
3893
|
+
import { join as join39 } from "path";
|
|
3878
3894
|
async function budgetOperation(directory, operation) {
|
|
3879
3895
|
const backend = openBackend(directory);
|
|
3880
|
-
const path =
|
|
3881
|
-
const metadata2 =
|
|
3896
|
+
const path = join39(directory, "runtime-policy.json");
|
|
3897
|
+
const metadata2 = object8(readPrivateJson2(backend.metadataPath));
|
|
3882
3898
|
const slice = budgetSlice(backend.owner);
|
|
3883
3899
|
if (operation.action === "configure") {
|
|
3884
3900
|
if (metadata2.budgetSlice || backend.records().length)
|
|
@@ -3900,7 +3916,7 @@ async function budgetOperation(directory, operation) {
|
|
|
3900
3916
|
]);
|
|
3901
3917
|
return { ok: true };
|
|
3902
3918
|
}
|
|
3903
|
-
const policy =
|
|
3919
|
+
const policy = object8(readPrivateJson2(path));
|
|
3904
3920
|
if (policy.owner !== backend.owner || metadata2.budgetSlice !== slice || policy.slice !== slice)
|
|
3905
3921
|
throw new Error("foreign_budget_policy");
|
|
3906
3922
|
if (operation.action === "cleanup") {
|
|
@@ -3928,7 +3944,7 @@ async function budgetOperation(directory, operation) {
|
|
|
3928
3944
|
});
|
|
3929
3945
|
if (active.length >= integer(policy.maxActive, "max_active", 1, 5))
|
|
3930
3946
|
throw new Error("runtime_capacity_exceeded");
|
|
3931
|
-
const spec =
|
|
3947
|
+
const spec = object8(operation.spec);
|
|
3932
3948
|
const memoryMb = integer(spec.memoryMb, "session_memory", 32, 1536);
|
|
3933
3949
|
const resource = spec.resource === void 0 ? void 0 : resourceClaim(spec.resource, backend.owner);
|
|
3934
3950
|
const dockerPreparation = spec.dockerPreparation;
|
|
@@ -3978,7 +3994,7 @@ var init_budget = __esm({
|
|
|
3978
3994
|
|
|
3979
3995
|
// src/harness/adapters/systemd/configure.ts
|
|
3980
3996
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
3981
|
-
import { join as
|
|
3997
|
+
import { join as join40 } from "path";
|
|
3982
3998
|
function configureBudget(context, input) {
|
|
3983
3999
|
const { store, backend } = context;
|
|
3984
4000
|
assertOwnedRecords(backend);
|
|
@@ -4000,9 +4016,9 @@ function configureBudget(context, input) {
|
|
|
4000
4016
|
throw new Error("capacity_below_active_sessions");
|
|
4001
4017
|
if (active.reduce((total, session) => total + session.memoryMb, 0) > memoryMb)
|
|
4002
4018
|
throw new Error("budget_below_reserved_memory");
|
|
4003
|
-
const path =
|
|
4004
|
-
const policy =
|
|
4005
|
-
const metadata2 =
|
|
4019
|
+
const path = join40(backend.runDirectory, "runtime-policy.json");
|
|
4020
|
+
const policy = object8(readPrivateJson2(path));
|
|
4021
|
+
const metadata2 = object8(readPrivateJson2(backend.metadataPath));
|
|
4006
4022
|
const slice = budgetSlice(backend.owner);
|
|
4007
4023
|
if (policy.owner !== backend.owner || policy.slice !== slice || metadata2.budgetSlice !== slice)
|
|
4008
4024
|
throw new Error("foreign_budget_policy");
|
|
@@ -4037,14 +4053,14 @@ var init_configure = __esm({
|
|
|
4037
4053
|
});
|
|
4038
4054
|
|
|
4039
4055
|
// src/harness/adapters/process/executable.ts
|
|
4040
|
-
import { accessSync, constants as constants6, statSync as
|
|
4041
|
-
import { delimiter, resolve as
|
|
4056
|
+
import { accessSync, constants as constants6, statSync as statSync16 } from "fs";
|
|
4057
|
+
import { delimiter, resolve as resolve19 } from "path";
|
|
4042
4058
|
function locateExecutable(binary, cwd = process.cwd()) {
|
|
4043
4059
|
for (const directory of (process.env.PATH ?? "").split(delimiter)) {
|
|
4044
4060
|
if (!directory) continue;
|
|
4045
|
-
const path =
|
|
4061
|
+
const path = resolve19(cwd, directory, binary);
|
|
4046
4062
|
try {
|
|
4047
|
-
if (!
|
|
4063
|
+
if (!statSync16(path).isFile()) continue;
|
|
4048
4064
|
accessSync(path, constants6.X_OK);
|
|
4049
4065
|
return path;
|
|
4050
4066
|
} catch {
|
|
@@ -4095,11 +4111,11 @@ var init_admission2 = __esm({
|
|
|
4095
4111
|
|
|
4096
4112
|
// src/harness/groups/service.ts
|
|
4097
4113
|
import { randomUUID as randomUUID9 } from "crypto";
|
|
4098
|
-
import { realpathSync as
|
|
4099
|
-
import { join as
|
|
4114
|
+
import { realpathSync as realpathSync10, statSync as statSync17, unlinkSync as unlinkSync4 } from "fs";
|
|
4115
|
+
import { join as join41 } from "path";
|
|
4100
4116
|
async function createGroup(input) {
|
|
4101
|
-
const projectRoot =
|
|
4102
|
-
if (!
|
|
4117
|
+
const projectRoot = realpathSync10(text(input.projectRoot, "project_root"));
|
|
4118
|
+
if (!statSync17(projectRoot).isDirectory())
|
|
4103
4119
|
throw new Error("invalid_project_root");
|
|
4104
4120
|
const allowedRuntimes = strings2(input.allowedRuntimes, "allowed_runtimes");
|
|
4105
4121
|
const profile = input.profile ?? "native";
|
|
@@ -4129,7 +4145,7 @@ async function createGroup(input) {
|
|
|
4129
4145
|
sessions: [],
|
|
4130
4146
|
...dockerPolicy2 ? { dockerPolicy: dockerPolicy2 } : {}
|
|
4131
4147
|
};
|
|
4132
|
-
const groupFile =
|
|
4148
|
+
const groupFile = join41(backend.runDirectory, "group.json");
|
|
4133
4149
|
privateJson(groupFile, { group, leases: [], deliveries: [] });
|
|
4134
4150
|
try {
|
|
4135
4151
|
await budgetOperation(backend.runDirectory, {
|
|
@@ -4142,7 +4158,7 @@ async function createGroup(input) {
|
|
|
4142
4158
|
await budgetOperation(backend.runDirectory, { action: "cleanup" });
|
|
4143
4159
|
unlinkSync4(groupFile);
|
|
4144
4160
|
} catch (cleanupError) {
|
|
4145
|
-
privateJson(
|
|
4161
|
+
privateJson(join41(backend.runDirectory, "initialization-failure.json"), {
|
|
4146
4162
|
error: String(error),
|
|
4147
4163
|
cleanupError: String(cleanupError),
|
|
4148
4164
|
recoveryRequired: true
|
|
@@ -4204,16 +4220,16 @@ var init_service2 = __esm({
|
|
|
4204
4220
|
});
|
|
4205
4221
|
|
|
4206
4222
|
// src/harness/groups/catalog.ts
|
|
4207
|
-
import { existsSync as
|
|
4208
|
-
import { join as
|
|
4223
|
+
import { existsSync as existsSync27, readdirSync as readdirSync12 } from "fs";
|
|
4224
|
+
import { join as join42 } from "path";
|
|
4209
4225
|
function listGroups() {
|
|
4210
4226
|
const directory = stateDirectory();
|
|
4211
|
-
if (!
|
|
4227
|
+
if (!existsSync27(directory)) return { groups: [] };
|
|
4212
4228
|
const groups2 = [];
|
|
4213
|
-
for (const entry2 of
|
|
4229
|
+
for (const entry2 of readdirSync12(directory, { withFileTypes: true })) {
|
|
4214
4230
|
if (!entry2.isDirectory()) continue;
|
|
4215
|
-
const path =
|
|
4216
|
-
if (!
|
|
4231
|
+
const path = join42(directory, entry2.name, "group.json");
|
|
4232
|
+
if (!existsSync27(path)) continue;
|
|
4217
4233
|
try {
|
|
4218
4234
|
const store = new GroupStore(path);
|
|
4219
4235
|
groups2.push({ groupFile: store.path, group: store.state.group });
|
|
@@ -4272,10 +4288,10 @@ var init_service3 = __esm({
|
|
|
4272
4288
|
});
|
|
4273
4289
|
|
|
4274
4290
|
// src/harness/application/context.ts
|
|
4275
|
-
import { dirname as
|
|
4291
|
+
import { dirname as dirname21 } from "path";
|
|
4276
4292
|
function openGroup(path) {
|
|
4277
4293
|
const store = new GroupStore(path);
|
|
4278
|
-
const backend = openBackend(
|
|
4294
|
+
const backend = openBackend(dirname21(store.path));
|
|
4279
4295
|
if (store.state.group.owner !== backend.owner)
|
|
4280
4296
|
throw new Error("foreign_group");
|
|
4281
4297
|
return { store, backend };
|
|
@@ -4390,7 +4406,7 @@ async function watchSession(operation) {
|
|
|
4390
4406
|
)
|
|
4391
4407
|
});
|
|
4392
4408
|
if (index + 1 < count)
|
|
4393
|
-
await new Promise((
|
|
4409
|
+
await new Promise((resolve22) => setTimeout(resolve22, intervalMs));
|
|
4394
4410
|
}
|
|
4395
4411
|
return {
|
|
4396
4412
|
schemaVersion: 1,
|
|
@@ -4410,9 +4426,9 @@ var init_watch = __esm({
|
|
|
4410
4426
|
|
|
4411
4427
|
// src/harness/control/leases.ts
|
|
4412
4428
|
import { randomUUID as randomUUID10 } from "crypto";
|
|
4413
|
-
import { join as
|
|
4429
|
+
import { join as join43 } from "path";
|
|
4414
4430
|
function resolveLease(context, file2, allowExpired = false) {
|
|
4415
|
-
const credential2 =
|
|
4431
|
+
const credential2 = object8(readPrivateJson2(file2));
|
|
4416
4432
|
const lease = context.store.state.leases.find(
|
|
4417
4433
|
(entry2) => entry2.token === credential2.token
|
|
4418
4434
|
);
|
|
@@ -4454,7 +4470,7 @@ function acquireControl(context, sessionId, controller, mode) {
|
|
|
4454
4470
|
);
|
|
4455
4471
|
store.state.leases.push(lease);
|
|
4456
4472
|
store.save();
|
|
4457
|
-
const leaseFile =
|
|
4473
|
+
const leaseFile = join43(backend.runDirectory, `lease-${randomUUID10()}.json`);
|
|
4458
4474
|
privateJson(leaseFile, { token: lease.token });
|
|
4459
4475
|
return { sessionId, controller, mode, expiresAt: lease.expiresAt, leaseFile };
|
|
4460
4476
|
}
|
|
@@ -4507,7 +4523,7 @@ var init_leases = __esm({
|
|
|
4507
4523
|
});
|
|
4508
4524
|
|
|
4509
4525
|
// src/harness/delivery/service.ts
|
|
4510
|
-
import { createHash as
|
|
4526
|
+
import { createHash as createHash18 } from "crypto";
|
|
4511
4527
|
function deliver(context, operation, leaseFile, requestId, text3) {
|
|
4512
4528
|
const { store, backend } = context;
|
|
4513
4529
|
const lease = resolveLease(context, leaseFile);
|
|
@@ -4523,7 +4539,7 @@ function deliver(context, operation, leaseFile, requestId, text3) {
|
|
|
4523
4539
|
if (/[\r\n\t]/.test(text3))
|
|
4524
4540
|
throw new Error("unqualified_terminal_control_input");
|
|
4525
4541
|
id(requestId, "request_id");
|
|
4526
|
-
const digest2 =
|
|
4542
|
+
const digest2 = createHash18("sha256").update(
|
|
4527
4543
|
JSON.stringify({
|
|
4528
4544
|
operation,
|
|
4529
4545
|
text: text3,
|
|
@@ -4601,18 +4617,18 @@ var init_service5 = __esm({
|
|
|
4601
4617
|
});
|
|
4602
4618
|
|
|
4603
4619
|
// src/harness/adapters/process/recover-lock.ts
|
|
4604
|
-
import { existsSync as
|
|
4605
|
-
import { join as
|
|
4620
|
+
import { existsSync as existsSync28, lstatSync as lstatSync10, unlinkSync as unlinkSync5 } from "fs";
|
|
4621
|
+
import { join as join44 } from "path";
|
|
4606
4622
|
function recoverInputLock(backend, sessionId) {
|
|
4607
4623
|
backend.inspect(sessionId);
|
|
4608
|
-
const path =
|
|
4609
|
-
if (!
|
|
4610
|
-
const before =
|
|
4611
|
-
const record =
|
|
4624
|
+
const path = join44(backend.runDirectory, `${sessionId}.input.lock`);
|
|
4625
|
+
if (!existsSync28(path)) return;
|
|
4626
|
+
const before = lstatSync10(path);
|
|
4627
|
+
const record = object8(readPrivateJson2(path));
|
|
4612
4628
|
if (record.id !== sessionId) throw new Error("foreign_writer_lock");
|
|
4613
4629
|
if (!controllerExited(text(record.controller, "controller_identity")))
|
|
4614
4630
|
throw new Error("writer_still_alive");
|
|
4615
|
-
if (
|
|
4631
|
+
if (lstatSync10(path).ino !== before.ino)
|
|
4616
4632
|
throw new Error("writer_lock_changed");
|
|
4617
4633
|
unlinkSync5(path);
|
|
4618
4634
|
}
|
|
@@ -4626,7 +4642,7 @@ var init_recover_lock = __esm({
|
|
|
4626
4642
|
});
|
|
4627
4643
|
|
|
4628
4644
|
// src/harness/launch/service.ts
|
|
4629
|
-
import { existsSync as
|
|
4645
|
+
import { existsSync as existsSync29 } from "fs";
|
|
4630
4646
|
function assertLaunchAdmission(records2) {
|
|
4631
4647
|
for (const journal of launchJournals(records2)) {
|
|
4632
4648
|
if (journal.abortRecord()?.state === "stopped" && unitActive(journal.intent().unit))
|
|
@@ -4651,12 +4667,12 @@ function inspectPendingStarts(context) {
|
|
|
4651
4667
|
role: journal.intent().role,
|
|
4652
4668
|
runtime: journal.intent().runtime,
|
|
4653
4669
|
controllerExited: controllerExited(journal.intent().controllerIdentity),
|
|
4654
|
-
proofPresent:
|
|
4655
|
-
recordPresent:
|
|
4670
|
+
proofPresent: existsSync29(journal.path("proof")),
|
|
4671
|
+
recordPresent: existsSync29(journal.records.path(journal.id)),
|
|
4656
4672
|
registered: context.store.state.group.sessions.some(
|
|
4657
4673
|
(session) => session.id === journal.id
|
|
4658
4674
|
),
|
|
4659
|
-
authorizationPresent:
|
|
4675
|
+
authorizationPresent: existsSync29(journal.path("grant")),
|
|
4660
4676
|
processingConfirmed: false,
|
|
4661
4677
|
inputReplayed: false
|
|
4662
4678
|
}));
|
|
@@ -4674,8 +4690,8 @@ var init_service6 = __esm({
|
|
|
4674
4690
|
});
|
|
4675
4691
|
|
|
4676
4692
|
// src/harness/sessions/service.ts
|
|
4677
|
-
import { realpathSync as
|
|
4678
|
-
import { relative as
|
|
4693
|
+
import { realpathSync as realpathSync11 } from "fs";
|
|
4694
|
+
import { relative as relative15 } from "path";
|
|
4679
4695
|
async function startSession(context, input) {
|
|
4680
4696
|
const { store, backend } = context;
|
|
4681
4697
|
const group = store.state.group;
|
|
@@ -4708,8 +4724,8 @@ async function startSession(context, input) {
|
|
|
4708
4724
|
);
|
|
4709
4725
|
if (!backend.inspect(main2.id).alive) throw new Error("main_unavailable");
|
|
4710
4726
|
}
|
|
4711
|
-
const cwd =
|
|
4712
|
-
const scope =
|
|
4727
|
+
const cwd = realpathSync11(text(input.cwd ?? group.projectRoot, "cwd"));
|
|
4728
|
+
const scope = relative15(group.projectRoot, cwd);
|
|
4713
4729
|
if (scope === ".." || scope.startsWith("../"))
|
|
4714
4730
|
throw new Error("cwd_outside_project");
|
|
4715
4731
|
const memoryMb = integer(input.memoryMb ?? 384, "session_memory", 32, 1536);
|
|
@@ -5008,7 +5024,7 @@ async function runOperation(operation) {
|
|
|
5008
5024
|
return groupOperation(context, action, operation.input);
|
|
5009
5025
|
if (family === "session") {
|
|
5010
5026
|
if (action === "start")
|
|
5011
|
-
return startSession(context,
|
|
5027
|
+
return startSession(context, object8(operation.input));
|
|
5012
5028
|
if (action === "list") return listSessions(context);
|
|
5013
5029
|
const sessionId = text(operation.sessionId, "session_id");
|
|
5014
5030
|
if (action === "stop") return stopSession(context, sessionId);
|
|
@@ -5066,7 +5082,7 @@ async function runOperation(operation) {
|
|
|
5066
5082
|
if (action === "stop-orphan")
|
|
5067
5083
|
return stopOrphan(context, text(operation.sessionId, "session_id"));
|
|
5068
5084
|
if (action === "replace-main")
|
|
5069
|
-
return replaceMain(context,
|
|
5085
|
+
return replaceMain(context, object8(operation.input));
|
|
5070
5086
|
}
|
|
5071
5087
|
throw new Error("unsupported_operation");
|
|
5072
5088
|
}
|
|
@@ -5141,7 +5157,7 @@ async function invoke(operation) {
|
|
|
5141
5157
|
return {
|
|
5142
5158
|
schemaVersion: 1,
|
|
5143
5159
|
ok: true,
|
|
5144
|
-
...await createGroup(
|
|
5160
|
+
...await createGroup(object8(operation.input))
|
|
5145
5161
|
};
|
|
5146
5162
|
}
|
|
5147
5163
|
if (operation.family === "attach" && operation.action === "open") {
|
|
@@ -5171,9 +5187,9 @@ var init_invoke = __esm({
|
|
|
5171
5187
|
});
|
|
5172
5188
|
|
|
5173
5189
|
// src/tui/adapters/process/runtime.ts
|
|
5174
|
-
import { dirname as
|
|
5175
|
-
import { fileURLToPath as
|
|
5176
|
-
import { existsSync as
|
|
5190
|
+
import { dirname as dirname22, join as join45 } from "path";
|
|
5191
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
5192
|
+
import { existsSync as existsSync30, realpathSync as realpathSync12 } from "fs";
|
|
5177
5193
|
function dependencies() {
|
|
5178
5194
|
const tmux = locateExecutable("tmux");
|
|
5179
5195
|
const flock = locateExecutable("flock");
|
|
@@ -5205,11 +5221,11 @@ function runtime() {
|
|
|
5205
5221
|
const available = dependencies();
|
|
5206
5222
|
if (!available.supported || !available.node)
|
|
5207
5223
|
throw new Error("tui_dependencies_unavailable");
|
|
5208
|
-
const current =
|
|
5209
|
-
const directory =
|
|
5210
|
-
const bundled =
|
|
5211
|
-
if (!
|
|
5212
|
-
return { node:
|
|
5224
|
+
const current = fileURLToPath4(import.meta.url);
|
|
5225
|
+
const directory = dirname22(current);
|
|
5226
|
+
const bundled = join45(directory, "tui-worker.js");
|
|
5227
|
+
if (!existsSync30(bundled)) throw new Error("tui_build_required");
|
|
5228
|
+
return { node: realpathSync12(available.node), worker: bundled };
|
|
5213
5229
|
}
|
|
5214
5230
|
var init_runtime2 = __esm({
|
|
5215
5231
|
"src/tui/adapters/process/runtime.ts"() {
|
|
@@ -5223,32 +5239,32 @@ var init_runtime2 = __esm({
|
|
|
5223
5239
|
// src/tui/state/store.ts
|
|
5224
5240
|
import { randomUUID as randomUUID12 } from "crypto";
|
|
5225
5241
|
import {
|
|
5226
|
-
existsSync as
|
|
5227
|
-
lstatSync as
|
|
5242
|
+
existsSync as existsSync31,
|
|
5243
|
+
lstatSync as lstatSync11,
|
|
5228
5244
|
mkdirSync as mkdirSync11,
|
|
5229
|
-
realpathSync as
|
|
5245
|
+
realpathSync as realpathSync13,
|
|
5230
5246
|
unlinkSync as unlinkSync6,
|
|
5231
5247
|
writeFileSync as writeFileSync13
|
|
5232
5248
|
} from "fs";
|
|
5233
5249
|
import { homedir as homedir10 } from "os";
|
|
5234
|
-
import { dirname as
|
|
5250
|
+
import { dirname as dirname23, join as join46 } from "path";
|
|
5235
5251
|
function createDirectory() {
|
|
5236
|
-
const root =
|
|
5237
|
-
process.env.XDG_STATE_HOME ??
|
|
5252
|
+
const root = join46(
|
|
5253
|
+
process.env.XDG_STATE_HOME ?? join46(homedir10(), ".local/state"),
|
|
5238
5254
|
"pmcp/tui"
|
|
5239
5255
|
);
|
|
5240
5256
|
mkdirSync11(root, { recursive: true, mode: 448 });
|
|
5241
|
-
const directory =
|
|
5242
|
-
if (Buffer.byteLength(
|
|
5257
|
+
const directory = join46(realpathSync13(root), randomUUID12().slice(0, 16));
|
|
5258
|
+
if (Buffer.byteLength(join46(directory, "s")) > 100)
|
|
5243
5259
|
throw new Error("socket_path_too_long");
|
|
5244
5260
|
mkdirSync11(directory, { mode: 448 });
|
|
5245
5261
|
const owner = randomUUID12();
|
|
5246
|
-
privateJson(
|
|
5262
|
+
privateJson(join46(directory, "owner.json"), {
|
|
5247
5263
|
owner,
|
|
5248
5264
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5249
5265
|
});
|
|
5250
5266
|
writeFileSync13(
|
|
5251
|
-
|
|
5267
|
+
join46(directory, "tmux.conf"),
|
|
5252
5268
|
[
|
|
5253
5269
|
"set -g remain-on-exit on",
|
|
5254
5270
|
"set -g history-limit 2000",
|
|
@@ -5263,13 +5279,13 @@ function createDirectory() {
|
|
|
5263
5279
|
return { directory, owner };
|
|
5264
5280
|
}
|
|
5265
5281
|
function readView(path) {
|
|
5266
|
-
const file2 =
|
|
5267
|
-
const directory =
|
|
5268
|
-
const stat =
|
|
5282
|
+
const file2 = realpathSync13(path);
|
|
5283
|
+
const directory = dirname23(file2);
|
|
5284
|
+
const stat = lstatSync11(directory);
|
|
5269
5285
|
if (!stat.isDirectory() || stat.uid !== process.getuid?.() || (stat.mode & 63) !== 0)
|
|
5270
5286
|
throw new Error("unsafe_view_directory");
|
|
5271
|
-
const value =
|
|
5272
|
-
const metadata2 =
|
|
5287
|
+
const value = object8(readPrivateJson2(file2));
|
|
5288
|
+
const metadata2 = object8(readPrivateJson2(join46(directory, "owner.json")));
|
|
5273
5289
|
if (value.schemaVersion !== 1 || value.directory !== directory || value.owner !== metadata2.owner)
|
|
5274
5290
|
throw new Error("foreign_view_owner");
|
|
5275
5291
|
for (const key of [
|
|
@@ -5294,7 +5310,7 @@ function readView(path) {
|
|
|
5294
5310
|
"uncertain"
|
|
5295
5311
|
].includes(String(value.phase)))
|
|
5296
5312
|
throw new Error("invalid_control_phase");
|
|
5297
|
-
const plan2 =
|
|
5313
|
+
const plan2 = object8(value.plan);
|
|
5298
5314
|
planLayout(Number(plan2.columns), Number(plan2.rows));
|
|
5299
5315
|
for (const [key, pattern] of [
|
|
5300
5316
|
["session", /^\$\d+$/],
|
|
@@ -5306,10 +5322,10 @@ function readView(path) {
|
|
|
5306
5322
|
throw new Error("invalid_view_identity");
|
|
5307
5323
|
}
|
|
5308
5324
|
const view = value;
|
|
5309
|
-
const fallback =
|
|
5310
|
-
if (
|
|
5311
|
-
const recorded =
|
|
5312
|
-
const failure2 =
|
|
5325
|
+
const fallback = join46(directory, "callback-failure.json");
|
|
5326
|
+
if (existsSync31(fallback)) {
|
|
5327
|
+
const recorded = object8(readPrivateJson2(fallback));
|
|
5328
|
+
const failure2 = object8(recorded.failure);
|
|
5313
5329
|
if (recorded.owner !== view.owner || typeof failure2.code !== "string" || !/^[a-z][a-z0-9_]{0,79}$/.test(failure2.code))
|
|
5314
5330
|
throw new Error("invalid_view_failure");
|
|
5315
5331
|
if (!view.failure || String(failure2.at) >= view.failure.at)
|
|
@@ -5318,8 +5334,8 @@ function readView(path) {
|
|
|
5318
5334
|
return { file: file2, view };
|
|
5319
5335
|
}
|
|
5320
5336
|
function dismissFailure(file2, view) {
|
|
5321
|
-
const fallback =
|
|
5322
|
-
if (
|
|
5337
|
+
const fallback = join46(view.directory, "callback-failure.json");
|
|
5338
|
+
if (existsSync31(fallback)) unlinkSync6(fallback);
|
|
5323
5339
|
delete view.failure;
|
|
5324
5340
|
saveView(file2, view);
|
|
5325
5341
|
}
|
|
@@ -5381,7 +5397,7 @@ var init_lock = __esm({
|
|
|
5381
5397
|
});
|
|
5382
5398
|
|
|
5383
5399
|
// src/tui/adapters/process/budget.ts
|
|
5384
|
-
import { readFileSync as
|
|
5400
|
+
import { readFileSync as readFileSync36 } from "fs";
|
|
5385
5401
|
function viewUnit(owner) {
|
|
5386
5402
|
if (!/^[a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12}$/.test(owner))
|
|
5387
5403
|
throw new Error("invalid_view_owner");
|
|
@@ -5409,7 +5425,7 @@ function startPresentation(owner, args, env) {
|
|
|
5409
5425
|
);
|
|
5410
5426
|
}
|
|
5411
5427
|
function assertPresentationBudget(owner, pid) {
|
|
5412
|
-
if (!
|
|
5428
|
+
if (!readFileSync36(`/proc/${pid}/cgroup`, "utf8").includes(
|
|
5413
5429
|
`/${viewUnit(owner)}
|
|
5414
5430
|
`
|
|
5415
5431
|
))
|
|
@@ -5424,7 +5440,7 @@ var init_budget2 = __esm({
|
|
|
5424
5440
|
|
|
5425
5441
|
// src/tui/adapters/tmux/connection.ts
|
|
5426
5442
|
import { spawn as spawn3 } from "child_process";
|
|
5427
|
-
import { join as
|
|
5443
|
+
import { join as join47 } from "path";
|
|
5428
5444
|
var ViewConnection;
|
|
5429
5445
|
var init_connection2 = __esm({
|
|
5430
5446
|
"src/tui/adapters/tmux/connection.ts"() {
|
|
@@ -5437,7 +5453,7 @@ var init_connection2 = __esm({
|
|
|
5437
5453
|
this.directory = directory;
|
|
5438
5454
|
this.owner = owner;
|
|
5439
5455
|
this.identity = identity;
|
|
5440
|
-
this.socket =
|
|
5456
|
+
this.socket = join47(directory, "s");
|
|
5441
5457
|
}
|
|
5442
5458
|
directory;
|
|
5443
5459
|
owner;
|
|
@@ -5450,14 +5466,14 @@ var init_connection2 = __esm({
|
|
|
5450
5466
|
"-S",
|
|
5451
5467
|
this.socket,
|
|
5452
5468
|
"-f",
|
|
5453
|
-
|
|
5469
|
+
join47(this.directory, "tmux.conf"),
|
|
5454
5470
|
...args
|
|
5455
5471
|
];
|
|
5456
5472
|
if (args[0] === "new-session" && !this.identity)
|
|
5457
5473
|
return startPresentation(this.owner, options2, env).trim();
|
|
5458
5474
|
return command(
|
|
5459
5475
|
"tmux",
|
|
5460
|
-
["-S", this.socket, "-f",
|
|
5476
|
+
["-S", this.socket, "-f", join47(this.directory, "tmux.conf"), ...args],
|
|
5461
5477
|
{ env }
|
|
5462
5478
|
).trim();
|
|
5463
5479
|
}
|
|
@@ -5511,9 +5527,9 @@ var init_connection2 = __esm({
|
|
|
5511
5527
|
["-S", this.socket, "attach-session", "-E", "-t", view.session],
|
|
5512
5528
|
{ stdio: "inherit", env }
|
|
5513
5529
|
);
|
|
5514
|
-
return new Promise((
|
|
5530
|
+
return new Promise((resolve22, reject) => {
|
|
5515
5531
|
client.once("error", reject);
|
|
5516
|
-
client.once("exit", (code) =>
|
|
5532
|
+
client.once("exit", (code) => resolve22(code ?? 1));
|
|
5517
5533
|
});
|
|
5518
5534
|
}
|
|
5519
5535
|
};
|
|
@@ -5521,7 +5537,7 @@ var init_connection2 = __esm({
|
|
|
5521
5537
|
});
|
|
5522
5538
|
|
|
5523
5539
|
// src/tui/application/feedback.ts
|
|
5524
|
-
import { join as
|
|
5540
|
+
import { join as join48 } from "path";
|
|
5525
5541
|
function describeFailure(action, error) {
|
|
5526
5542
|
const message = error instanceof Error ? error.message : "view_action_failed";
|
|
5527
5543
|
const code = error instanceof RangeError ? "terminal_size_invalid" : failureCode(message);
|
|
@@ -5919,16 +5935,16 @@ var init_common = __esm({
|
|
|
5919
5935
|
});
|
|
5920
5936
|
|
|
5921
5937
|
// src/tui/application/create.ts
|
|
5922
|
-
import { join as
|
|
5923
|
-
import { realpathSync as
|
|
5938
|
+
import { join as join49 } from "path";
|
|
5939
|
+
import { realpathSync as realpathSync14 } from "fs";
|
|
5924
5940
|
async function createView(input) {
|
|
5925
5941
|
const plan2 = planLayout(input.columns, input.rows);
|
|
5926
5942
|
const helpers = runtime();
|
|
5927
5943
|
const mailbox = input.mailbox ? {
|
|
5928
|
-
config:
|
|
5929
|
-
actorFile:
|
|
5944
|
+
config: realpathSync14(input.mailbox.config),
|
|
5945
|
+
actorFile: realpathSync14(input.mailbox.actorFile)
|
|
5930
5946
|
} : void 0;
|
|
5931
|
-
const groupFile =
|
|
5947
|
+
const groupFile = realpathSync14(input.groupFile);
|
|
5932
5948
|
const group = new GroupStore(groupFile).state.group;
|
|
5933
5949
|
const common2 = new CommonHarness({
|
|
5934
5950
|
groupFile,
|
|
@@ -5941,7 +5957,7 @@ async function createView(input) {
|
|
|
5941
5957
|
);
|
|
5942
5958
|
if (!main2) throw new Error("view_requires_live_main");
|
|
5943
5959
|
const { directory, owner } = createDirectory();
|
|
5944
|
-
const file2 =
|
|
5960
|
+
const file2 = join49(directory, "view.json");
|
|
5945
5961
|
const connection = new ViewConnection(directory, owner);
|
|
5946
5962
|
const argv = [helpers.node, helpers.worker, "attach", file2, main2.id];
|
|
5947
5963
|
const result = connection.raw([
|
|
@@ -6027,7 +6043,7 @@ async function waitFor(predicate, timeoutMs) {
|
|
|
6027
6043
|
const deadline = Date.now() + timeoutMs;
|
|
6028
6044
|
while (Date.now() < deadline) {
|
|
6029
6045
|
if (await predicate()) return;
|
|
6030
|
-
await new Promise((
|
|
6046
|
+
await new Promise((resolve22) => setTimeout(resolve22, 50));
|
|
6031
6047
|
}
|
|
6032
6048
|
throw new Error("writer_not_ready");
|
|
6033
6049
|
}
|
|
@@ -6169,7 +6185,7 @@ var init_navigation = __esm({
|
|
|
6169
6185
|
});
|
|
6170
6186
|
|
|
6171
6187
|
// src/tui/application/history.ts
|
|
6172
|
-
import { join as
|
|
6188
|
+
import { join as join50 } from "path";
|
|
6173
6189
|
async function observeHistory(context) {
|
|
6174
6190
|
const { view, renderer, common: common2, file: file2 } = context;
|
|
6175
6191
|
await releaseControl(context);
|
|
@@ -6177,7 +6193,7 @@ async function observeHistory(context) {
|
|
|
6177
6193
|
sessionId: view.target,
|
|
6178
6194
|
input: { lines: 2e3, maxBytes: 65536 }
|
|
6179
6195
|
});
|
|
6180
|
-
privateJson(
|
|
6196
|
+
privateJson(join50(view.directory, "history.json"), {
|
|
6181
6197
|
sessionId: view.target,
|
|
6182
6198
|
content: history.content,
|
|
6183
6199
|
truncated: history.truncated
|
|
@@ -6253,8 +6269,8 @@ var init_mailbox = __esm({
|
|
|
6253
6269
|
});
|
|
6254
6270
|
|
|
6255
6271
|
// src/tui/application/lifecycle.ts
|
|
6256
|
-
import { existsSync as
|
|
6257
|
-
import { join as
|
|
6272
|
+
import { existsSync as existsSync32, unlinkSync as unlinkSync7 } from "fs";
|
|
6273
|
+
import { join as join51 } from "path";
|
|
6258
6274
|
function resizeView({ view, renderer, input }) {
|
|
6259
6275
|
renderer.resize(view, planLayout(input.columns, input.rows));
|
|
6260
6276
|
}
|
|
@@ -6269,8 +6285,8 @@ async function stopTarget(context) {
|
|
|
6269
6285
|
async function closeView(context) {
|
|
6270
6286
|
await returnToMain(context);
|
|
6271
6287
|
context.renderer.connection.cleanup();
|
|
6272
|
-
const history =
|
|
6273
|
-
if (
|
|
6288
|
+
const history = join51(context.view.directory, "history.json");
|
|
6289
|
+
if (existsSync32(history)) unlinkSync7(history);
|
|
6274
6290
|
context.view.state = "closed";
|
|
6275
6291
|
saveView(context.file, context.view);
|
|
6276
6292
|
}
|
|
@@ -6888,16 +6904,16 @@ async function openEntryVectors(path, options2 = {}) {
|
|
|
6888
6904
|
|
|
6889
6905
|
// src/commands/reindex.ts
|
|
6890
6906
|
init_encoder();
|
|
6891
|
-
import { existsSync as
|
|
6892
|
-
import { dirname as
|
|
6907
|
+
import { existsSync as existsSync6, mkdirSync } from "fs";
|
|
6908
|
+
import { dirname as dirname5 } from "path";
|
|
6893
6909
|
|
|
6894
6910
|
// src/commands/embedding.ts
|
|
6895
|
-
import { resolve as
|
|
6911
|
+
import { resolve as resolve5 } from "path";
|
|
6896
6912
|
init_encoder();
|
|
6897
6913
|
|
|
6898
6914
|
// src/catalog.ts
|
|
6899
|
-
import { existsSync as
|
|
6900
|
-
import { basename as basename2, dirname as dirname2, isAbsolute, join as
|
|
6915
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync as readdirSync2 } from "fs";
|
|
6916
|
+
import { basename as basename2, dirname as dirname2, isAbsolute as isAbsolute2, join as join3, relative as relative2 } from "path";
|
|
6901
6917
|
|
|
6902
6918
|
// src/frontmatter.ts
|
|
6903
6919
|
import { parse } from "yaml";
|
|
@@ -7019,8 +7035,122 @@ function specProblems(front, directory) {
|
|
|
7019
7035
|
}
|
|
7020
7036
|
|
|
7021
7037
|
// src/marketplace.ts
|
|
7022
|
-
import { existsSync, readFileSync, readdirSync } from "fs";
|
|
7023
|
-
import { basename, dirname, join, resolve as
|
|
7038
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, readdirSync } from "fs";
|
|
7039
|
+
import { basename, dirname, join as join2, resolve as resolve3 } from "path";
|
|
7040
|
+
|
|
7041
|
+
// src/marketplace/local.ts
|
|
7042
|
+
import { existsSync, readFileSync, realpathSync, statSync } from "fs";
|
|
7043
|
+
import { isAbsolute, join, relative, resolve as resolve2, sep } from "path";
|
|
7044
|
+
function object(value) {
|
|
7045
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
7046
|
+
}
|
|
7047
|
+
function containedPath(root, path) {
|
|
7048
|
+
const result = resolve2(root, path);
|
|
7049
|
+
const lexical = relative(resolve2(root), result);
|
|
7050
|
+
if (isAbsolute(lexical) || lexical === ".." || lexical.startsWith(`..${sep}`))
|
|
7051
|
+
throw new Error("Path leaves the plugin root");
|
|
7052
|
+
const physical = relative(realpathSync(root), realpathSync(result));
|
|
7053
|
+
if (isAbsolute(physical) || physical === ".." || physical.startsWith(`..${sep}`))
|
|
7054
|
+
throw new Error("Path leaves the plugin root through a symlink");
|
|
7055
|
+
return result;
|
|
7056
|
+
}
|
|
7057
|
+
function pluginManifest(directory) {
|
|
7058
|
+
for (const path of [
|
|
7059
|
+
".claude-plugin/plugin.json",
|
|
7060
|
+
"plugin.json",
|
|
7061
|
+
".codex-plugin/plugin.json",
|
|
7062
|
+
".grok-plugin/plugin.json"
|
|
7063
|
+
]) {
|
|
7064
|
+
if (!existsSync(join(directory, path))) continue;
|
|
7065
|
+
const value = JSON.parse(
|
|
7066
|
+
readFileSync(containedPath(directory, path), "utf8")
|
|
7067
|
+
);
|
|
7068
|
+
if (!object(value)) throw new Error("Plugin manifest must be an object");
|
|
7069
|
+
return value;
|
|
7070
|
+
}
|
|
7071
|
+
return {};
|
|
7072
|
+
}
|
|
7073
|
+
function localSource(value) {
|
|
7074
|
+
if (typeof value === "string") return value;
|
|
7075
|
+
if (object(value) && value.source === "local" && typeof value.path === "string")
|
|
7076
|
+
return value.path;
|
|
7077
|
+
throw new Error(
|
|
7078
|
+
"Only installed local plugin sources can be read; use the native installer for remote sources"
|
|
7079
|
+
);
|
|
7080
|
+
}
|
|
7081
|
+
function readLocalMarketplace(directory, onReject) {
|
|
7082
|
+
const filename = [
|
|
7083
|
+
".claude-plugin/marketplace.json",
|
|
7084
|
+
".agents/plugins/marketplace.json",
|
|
7085
|
+
".grok-plugin/marketplace.json"
|
|
7086
|
+
].find((path2) => existsSync(join(directory, path2)));
|
|
7087
|
+
if (!filename) {
|
|
7088
|
+
onReject?.({
|
|
7089
|
+
path: directory,
|
|
7090
|
+
reason: "No supported native marketplace.json found"
|
|
7091
|
+
});
|
|
7092
|
+
return null;
|
|
7093
|
+
}
|
|
7094
|
+
const path = join(directory, filename);
|
|
7095
|
+
let manifest2;
|
|
7096
|
+
try {
|
|
7097
|
+
manifest2 = JSON.parse(
|
|
7098
|
+
readFileSync(containedPath(directory, filename), "utf8")
|
|
7099
|
+
);
|
|
7100
|
+
} catch {
|
|
7101
|
+
onReject?.({
|
|
7102
|
+
path,
|
|
7103
|
+
reason: "marketplace.json is not valid JSON or is outside its root"
|
|
7104
|
+
});
|
|
7105
|
+
return null;
|
|
7106
|
+
}
|
|
7107
|
+
if (!object(manifest2) || !Array.isArray(manifest2.plugins)) {
|
|
7108
|
+
onReject?.({ path, reason: "marketplace.json requires a plugins array" });
|
|
7109
|
+
return null;
|
|
7110
|
+
}
|
|
7111
|
+
const name = typeof manifest2.name === "string" ? manifest2.name : "marketplace";
|
|
7112
|
+
if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/u.test(name)) {
|
|
7113
|
+
onReject?.({ path, reason: "Invalid marketplace name" });
|
|
7114
|
+
return null;
|
|
7115
|
+
}
|
|
7116
|
+
const plugins = [];
|
|
7117
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7118
|
+
for (const item of manifest2.plugins) {
|
|
7119
|
+
try {
|
|
7120
|
+
if (!object(item) || typeof item.name !== "string" || !/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/u.test(item.name))
|
|
7121
|
+
throw new Error("Invalid plugin name");
|
|
7122
|
+
if (seen.has(item.name)) throw new Error("Duplicate plugin name");
|
|
7123
|
+
const source2 = localSource(item.source);
|
|
7124
|
+
if (!source2.startsWith("./") || source2.split(/[\\/]/u).includes(".."))
|
|
7125
|
+
throw new Error("Local plugin source must be a contained ./ path");
|
|
7126
|
+
const pluginDirectory = containedPath(directory, source2);
|
|
7127
|
+
if (!statSync(pluginDirectory).isDirectory())
|
|
7128
|
+
throw new Error("Plugin source must be a directory");
|
|
7129
|
+
const metadata3 = pluginManifest(pluginDirectory);
|
|
7130
|
+
if (metadata3.name !== void 0 && metadata3.name !== item.name)
|
|
7131
|
+
throw new Error("Plugin manifest name differs from marketplace entry");
|
|
7132
|
+
seen.add(item.name);
|
|
7133
|
+
plugins.push({
|
|
7134
|
+
name: item.name,
|
|
7135
|
+
directory: pluginDirectory,
|
|
7136
|
+
manifest: metadata3
|
|
7137
|
+
});
|
|
7138
|
+
} catch (error) {
|
|
7139
|
+
onReject?.({
|
|
7140
|
+
path,
|
|
7141
|
+
reason: error instanceof Error ? error.message : "Invalid plugin source"
|
|
7142
|
+
});
|
|
7143
|
+
}
|
|
7144
|
+
}
|
|
7145
|
+
const metadata2 = object(manifest2.metadata) ? manifest2.metadata : {};
|
|
7146
|
+
return {
|
|
7147
|
+
name,
|
|
7148
|
+
plugins,
|
|
7149
|
+
...typeof metadata2.tier === "string" ? { tier: metadata2.tier } : {}
|
|
7150
|
+
};
|
|
7151
|
+
}
|
|
7152
|
+
|
|
7153
|
+
// src/marketplace.ts
|
|
7024
7154
|
function readMetadata(source2) {
|
|
7025
7155
|
return metadataOf(frontmatterObject(source2));
|
|
7026
7156
|
}
|
|
@@ -7033,16 +7163,27 @@ var asList = (value) => {
|
|
|
7033
7163
|
if (typeof value === "string") return [value];
|
|
7034
7164
|
return Array.isArray(value) ? value : [];
|
|
7035
7165
|
};
|
|
7036
|
-
function readJson(path) {
|
|
7166
|
+
function readJson(path, root) {
|
|
7037
7167
|
try {
|
|
7038
|
-
const parsed = JSON.parse(
|
|
7168
|
+
const parsed = JSON.parse(
|
|
7169
|
+
readFileSync2(containedPath(root, path), "utf8")
|
|
7170
|
+
);
|
|
7039
7171
|
return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
7040
7172
|
} catch {
|
|
7041
7173
|
return null;
|
|
7042
7174
|
}
|
|
7043
7175
|
}
|
|
7044
7176
|
function markdownEntry(context, path, fallbackSlug, shape) {
|
|
7045
|
-
|
|
7177
|
+
let source2;
|
|
7178
|
+
try {
|
|
7179
|
+
source2 = readFileSync2(containedPath(context.pluginDir, path), "utf8");
|
|
7180
|
+
} catch {
|
|
7181
|
+
context.onReject?.({
|
|
7182
|
+
path,
|
|
7183
|
+
reason: "Asset is unreadable or outside the plugin root"
|
|
7184
|
+
});
|
|
7185
|
+
return null;
|
|
7186
|
+
}
|
|
7046
7187
|
const front = readFrontmatter(source2);
|
|
7047
7188
|
const description = front["description"] ?? "";
|
|
7048
7189
|
if (description === "") {
|
|
@@ -7074,25 +7215,25 @@ function markdownEntry(context, path, fallbackSlug, shape) {
|
|
|
7074
7215
|
};
|
|
7075
7216
|
}
|
|
7076
7217
|
function skillEntries(context) {
|
|
7077
|
-
const dir =
|
|
7078
|
-
if (!
|
|
7218
|
+
const dir = join2(context.pluginDir, "skills");
|
|
7219
|
+
if (!existsSync2(dir)) return [];
|
|
7079
7220
|
const found = [];
|
|
7080
7221
|
for (const child of readdirSync(dir, { withFileTypes: true })) {
|
|
7081
7222
|
if (!child.isDirectory()) continue;
|
|
7082
|
-
const path =
|
|
7083
|
-
if (!
|
|
7223
|
+
const path = join2(dir, child.name, "SKILL.md");
|
|
7224
|
+
if (!existsSync2(path)) continue;
|
|
7084
7225
|
const entry2 = markdownEntry(context, path, child.name, {});
|
|
7085
7226
|
if (entry2) found.push(entry2);
|
|
7086
7227
|
}
|
|
7087
7228
|
return found;
|
|
7088
7229
|
}
|
|
7089
7230
|
function commandEntries(context) {
|
|
7090
|
-
const dir =
|
|
7091
|
-
if (!
|
|
7231
|
+
const dir = join2(context.pluginDir, "commands");
|
|
7232
|
+
if (!existsSync2(dir)) return [];
|
|
7092
7233
|
const found = [];
|
|
7093
7234
|
for (const child of readdirSync(dir, { withFileTypes: true })) {
|
|
7094
7235
|
if (!child.isFile() || !child.name.endsWith(".md")) continue;
|
|
7095
|
-
const path =
|
|
7236
|
+
const path = join2(dir, child.name);
|
|
7096
7237
|
const entry2 = markdownEntry(context, path, basename(child.name, ".md"), {
|
|
7097
7238
|
root: path
|
|
7098
7239
|
});
|
|
@@ -7101,22 +7242,22 @@ function commandEntries(context) {
|
|
|
7101
7242
|
return found;
|
|
7102
7243
|
}
|
|
7103
7244
|
function agentEntries(context) {
|
|
7104
|
-
const dir =
|
|
7105
|
-
if (!
|
|
7245
|
+
const dir = join2(context.pluginDir, "agents");
|
|
7246
|
+
if (!existsSync2(dir)) return [];
|
|
7106
7247
|
const found = [];
|
|
7107
7248
|
for (const child of readdirSync(dir, { withFileTypes: true })) {
|
|
7108
7249
|
let entry2 = null;
|
|
7109
7250
|
if (child.isFile() && child.name.endsWith(".md")) {
|
|
7110
|
-
const path =
|
|
7251
|
+
const path = join2(dir, child.name);
|
|
7111
7252
|
entry2 = markdownEntry(context, path, basename(child.name, ".md"), {
|
|
7112
7253
|
kind: "agent",
|
|
7113
7254
|
root: path,
|
|
7114
7255
|
namespace: "agents"
|
|
7115
7256
|
});
|
|
7116
|
-
} else if (child.isDirectory() &&
|
|
7257
|
+
} else if (child.isDirectory() && existsSync2(join2(dir, child.name, "agent.md"))) {
|
|
7117
7258
|
entry2 = markdownEntry(
|
|
7118
7259
|
context,
|
|
7119
|
-
|
|
7260
|
+
join2(dir, child.name, "agent.md"),
|
|
7120
7261
|
child.name,
|
|
7121
7262
|
{
|
|
7122
7263
|
kind: "agent",
|
|
@@ -7130,9 +7271,9 @@ function agentEntries(context) {
|
|
|
7130
7271
|
}
|
|
7131
7272
|
function hookEntries(context, manifest2) {
|
|
7132
7273
|
const declared = typeof manifest2.hooks === "string" ? manifest2.hooks : void 0;
|
|
7133
|
-
const path =
|
|
7134
|
-
if (!
|
|
7135
|
-
const hooks = readJson(path);
|
|
7274
|
+
const path = resolve3(context.pluginDir, declared ?? "hooks/hooks.json");
|
|
7275
|
+
if (!existsSync2(path)) return [];
|
|
7276
|
+
const hooks = readJson(path, context.pluginDir);
|
|
7136
7277
|
if (hooks === null) {
|
|
7137
7278
|
context.onReject?.({ path, reason: "hooks.json is not a JSON object" });
|
|
7138
7279
|
return [];
|
|
@@ -7158,9 +7299,12 @@ function hookEntries(context, manifest2) {
|
|
|
7158
7299
|
];
|
|
7159
7300
|
}
|
|
7160
7301
|
function mcpEntries(context) {
|
|
7161
|
-
const
|
|
7162
|
-
|
|
7163
|
-
|
|
7302
|
+
const filename = [".mcp.json", "mcp.json", "mcp_config.json"].find(
|
|
7303
|
+
(name) => existsSync2(join2(context.pluginDir, name))
|
|
7304
|
+
);
|
|
7305
|
+
if (!filename) return [];
|
|
7306
|
+
const path = join2(context.pluginDir, filename);
|
|
7307
|
+
const config = readJson(path, context.pluginDir);
|
|
7164
7308
|
const servers = config?.["mcpServers"];
|
|
7165
7309
|
if (servers === null || typeof servers !== "object" || Array.isArray(servers)) {
|
|
7166
7310
|
context.onReject?.({ path, reason: ".mcp.json has no mcpServers object" });
|
|
@@ -7179,48 +7323,24 @@ function mcpEntries(context) {
|
|
|
7179
7323
|
}));
|
|
7180
7324
|
}
|
|
7181
7325
|
function readMarketplace(dir, onReject) {
|
|
7182
|
-
const
|
|
7183
|
-
if (!
|
|
7184
|
-
onReject?.({
|
|
7185
|
-
path: dir,
|
|
7186
|
-
reason: "no .claude-plugin/marketplace.json here"
|
|
7187
|
-
});
|
|
7188
|
-
return [];
|
|
7189
|
-
}
|
|
7190
|
-
let manifest2;
|
|
7191
|
-
try {
|
|
7192
|
-
manifest2 = JSON.parse(
|
|
7193
|
-
readFileSync(manifestPath, "utf8")
|
|
7194
|
-
);
|
|
7195
|
-
} catch {
|
|
7196
|
-
onReject?.({
|
|
7197
|
-
path: manifestPath,
|
|
7198
|
-
reason: "marketplace.json is not valid JSON"
|
|
7199
|
-
});
|
|
7200
|
-
return [];
|
|
7201
|
-
}
|
|
7202
|
-
const marketplace = manifest2.name ?? "marketplace";
|
|
7326
|
+
const marketplace = readLocalMarketplace(dir, onReject);
|
|
7327
|
+
if (!marketplace) return [];
|
|
7203
7328
|
const found = [];
|
|
7204
|
-
for (const plugin of
|
|
7205
|
-
|
|
7206
|
-
const pluginDir = resolve2(dir, plugin.source);
|
|
7207
|
-
if (!existsSync(pluginDir)) continue;
|
|
7208
|
-
const pluginManifest = readJson(
|
|
7209
|
-
join(pluginDir, ".claude-plugin", "plugin.json")
|
|
7210
|
-
) ?? {};
|
|
7329
|
+
for (const plugin of marketplace.plugins) {
|
|
7330
|
+
const pluginManifest2 = plugin.manifest;
|
|
7211
7331
|
const context = {
|
|
7212
|
-
marketplace,
|
|
7332
|
+
marketplace: marketplace.name,
|
|
7213
7333
|
plugin: plugin.name,
|
|
7214
|
-
pluginDir,
|
|
7215
|
-
pluginDescription: typeof
|
|
7216
|
-
tier:
|
|
7334
|
+
pluginDir: plugin.directory,
|
|
7335
|
+
pluginDescription: typeof pluginManifest2.description === "string" ? pluginManifest2.description : void 0,
|
|
7336
|
+
tier: marketplace.tier,
|
|
7217
7337
|
onReject
|
|
7218
7338
|
};
|
|
7219
7339
|
found.push(
|
|
7220
7340
|
...skillEntries(context),
|
|
7221
7341
|
...commandEntries(context),
|
|
7222
7342
|
...agentEntries(context),
|
|
7223
|
-
...hookEntries(context,
|
|
7343
|
+
...hookEntries(context, pluginManifest2),
|
|
7224
7344
|
...mcpEntries(context)
|
|
7225
7345
|
);
|
|
7226
7346
|
}
|
|
@@ -7262,29 +7382,29 @@ function skillFiles(packageDir) {
|
|
|
7262
7382
|
}
|
|
7263
7383
|
for (const entry2 of entries) {
|
|
7264
7384
|
if (entry2.isFile()) {
|
|
7265
|
-
if (entry2.name === "SKILL.md") found.push(
|
|
7385
|
+
if (entry2.name === "SKILL.md") found.push(join3(dir, entry2.name));
|
|
7266
7386
|
continue;
|
|
7267
7387
|
}
|
|
7268
7388
|
if (!entry2.isDirectory()) continue;
|
|
7269
7389
|
if (SKIPPED_DIRS.has(entry2.name)) continue;
|
|
7270
7390
|
if (depth + 1 <= MAX_SKILL_DEPTH)
|
|
7271
|
-
stack.push([
|
|
7391
|
+
stack.push([join3(dir, entry2.name), depth + 1]);
|
|
7272
7392
|
}
|
|
7273
7393
|
}
|
|
7274
7394
|
return found.sort();
|
|
7275
7395
|
}
|
|
7276
7396
|
function packageDirs(root) {
|
|
7277
|
-
if (!
|
|
7397
|
+
if (!existsSync3(root)) return [];
|
|
7278
7398
|
const found = [];
|
|
7279
7399
|
for (const entry2 of readdirSync2(root, { withFileTypes: true })) {
|
|
7280
7400
|
if (!entry2.isDirectory() && !entry2.isSymbolicLink()) continue;
|
|
7281
7401
|
if (entry2.name === ".bin" || entry2.name === ".cache") continue;
|
|
7282
|
-
const path =
|
|
7402
|
+
const path = join3(root, entry2.name);
|
|
7283
7403
|
if (entry2.name.startsWith("@")) {
|
|
7284
|
-
if (!
|
|
7404
|
+
if (!existsSync3(path)) continue;
|
|
7285
7405
|
for (const scoped of readdirSync2(path, { withFileTypes: true })) {
|
|
7286
7406
|
if (scoped.isDirectory() || scoped.isSymbolicLink()) {
|
|
7287
|
-
found.push(
|
|
7407
|
+
found.push(join3(path, scoped.name));
|
|
7288
7408
|
}
|
|
7289
7409
|
}
|
|
7290
7410
|
continue;
|
|
@@ -7296,7 +7416,7 @@ function packageDirs(root) {
|
|
|
7296
7416
|
function readManifest(dir) {
|
|
7297
7417
|
try {
|
|
7298
7418
|
const parsed = JSON.parse(
|
|
7299
|
-
|
|
7419
|
+
readFileSync3(join3(dir, "package.json"), "utf8")
|
|
7300
7420
|
);
|
|
7301
7421
|
return parsed !== null && typeof parsed === "object" ? parsed : null;
|
|
7302
7422
|
} catch {
|
|
@@ -7330,8 +7450,8 @@ function globDirs(base, pattern) {
|
|
|
7330
7450
|
}
|
|
7331
7451
|
const segment = segments[index];
|
|
7332
7452
|
if (!segment.includes("*")) {
|
|
7333
|
-
const next =
|
|
7334
|
-
if (
|
|
7453
|
+
const next = join3(dir, segment);
|
|
7454
|
+
if (existsSync3(next)) walk(next, index + 1);
|
|
7335
7455
|
return;
|
|
7336
7456
|
}
|
|
7337
7457
|
let entries;
|
|
@@ -7343,12 +7463,12 @@ function globDirs(base, pattern) {
|
|
|
7343
7463
|
const children = entries.filter((e) => e.isDirectory() || e.isSymbolicLink()).filter((e) => !SKIPPED_DIRS.has(e.name));
|
|
7344
7464
|
if (segment === "**") {
|
|
7345
7465
|
walk(dir, index + 1);
|
|
7346
|
-
for (const child of children) walk(
|
|
7466
|
+
for (const child of children) walk(join3(dir, child.name), index);
|
|
7347
7467
|
return;
|
|
7348
7468
|
}
|
|
7349
7469
|
const matcher = segmentMatcher(segment);
|
|
7350
7470
|
for (const child of children) {
|
|
7351
|
-
if (matcher.test(child.name)) walk(
|
|
7471
|
+
if (matcher.test(child.name)) walk(join3(dir, child.name), index + 1);
|
|
7352
7472
|
}
|
|
7353
7473
|
};
|
|
7354
7474
|
walk(base, 0);
|
|
@@ -7373,7 +7493,7 @@ function workspacePackageDirs(root) {
|
|
|
7373
7493
|
}
|
|
7374
7494
|
}
|
|
7375
7495
|
}
|
|
7376
|
-
const members = [...included].filter((m) => !excluded.has(m) &&
|
|
7496
|
+
const members = [...included].filter((m) => !excluded.has(m) && existsSync3(join3(m, "package.json"))).sort();
|
|
7377
7497
|
for (const member2 of members) expand(member2);
|
|
7378
7498
|
ordered.push(dir);
|
|
7379
7499
|
};
|
|
@@ -7390,9 +7510,9 @@ function nameOf(dir, named2) {
|
|
|
7390
7510
|
}
|
|
7391
7511
|
function expandRootGlob(dir) {
|
|
7392
7512
|
if (!dir.includes("*")) return [dir];
|
|
7393
|
-
const absolute2 =
|
|
7513
|
+
const absolute2 = isAbsolute2(dir);
|
|
7394
7514
|
const base = absolute2 ? "/" : ".";
|
|
7395
|
-
return globDirs(base, dir).filter((d) =>
|
|
7515
|
+
return globDirs(base, dir).filter((d) => existsSync3(join3(d, "package.json"))).sort();
|
|
7396
7516
|
}
|
|
7397
7517
|
function readCatalog(options2) {
|
|
7398
7518
|
const scopes = options2.scopes ?? [];
|
|
@@ -7416,7 +7536,7 @@ function readCatalog(options2) {
|
|
|
7416
7536
|
seenPaths.add(path);
|
|
7417
7537
|
let source2;
|
|
7418
7538
|
try {
|
|
7419
|
-
source2 =
|
|
7539
|
+
source2 = readFileSync3(path, "utf8");
|
|
7420
7540
|
} catch {
|
|
7421
7541
|
continue;
|
|
7422
7542
|
}
|
|
@@ -7460,7 +7580,7 @@ function readCatalog(options2) {
|
|
|
7460
7580
|
}
|
|
7461
7581
|
seen.add(entry2.name);
|
|
7462
7582
|
if (kindOf(entry2) === "skill" && basename2(entry2.path) === "SKILL.md") {
|
|
7463
|
-
const parsed = frontmatterObject(
|
|
7583
|
+
const parsed = frontmatterObject(readFileSync3(entry2.path, "utf8"));
|
|
7464
7584
|
for (const problem of specProblems(
|
|
7465
7585
|
parsed,
|
|
7466
7586
|
basename2(dirname2(entry2.path))
|
|
@@ -7482,6 +7602,28 @@ function withoutBody({
|
|
|
7482
7602
|
|
|
7483
7603
|
// src/commands/roots.ts
|
|
7484
7604
|
init_config();
|
|
7605
|
+
|
|
7606
|
+
// src/marketplace/builtin.ts
|
|
7607
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
|
|
7608
|
+
import { dirname as dirname4, join as join5 } from "path";
|
|
7609
|
+
import { fileURLToPath } from "url";
|
|
7610
|
+
function publicMarketplaceRoot(start2 = dirname4(fileURLToPath(import.meta.url))) {
|
|
7611
|
+
let directory = start2;
|
|
7612
|
+
for (; ; ) {
|
|
7613
|
+
const manifest2 = join5(directory, "package.json");
|
|
7614
|
+
if (existsSync5(manifest2)) {
|
|
7615
|
+
const value = JSON.parse(readFileSync5(manifest2, "utf8"));
|
|
7616
|
+
if (value !== null && typeof value === "object" && "name" in value && value.name === "@modootoday/pmcp")
|
|
7617
|
+
return directory;
|
|
7618
|
+
}
|
|
7619
|
+
const parent = dirname4(directory);
|
|
7620
|
+
if (parent === directory)
|
|
7621
|
+
throw new Error("Cannot locate the bundled PMCP marketplace");
|
|
7622
|
+
directory = parent;
|
|
7623
|
+
}
|
|
7624
|
+
}
|
|
7625
|
+
|
|
7626
|
+
// src/commands/roots.ts
|
|
7485
7627
|
var CATALOG_OPTIONS = [
|
|
7486
7628
|
{
|
|
7487
7629
|
name: "root",
|
|
@@ -7495,9 +7637,14 @@ var CATALOG_OPTIONS = [
|
|
|
7495
7637
|
repeat: true,
|
|
7496
7638
|
placeholder: "<prefix>"
|
|
7497
7639
|
},
|
|
7640
|
+
{
|
|
7641
|
+
name: "no-builtin",
|
|
7642
|
+
describe: "Exclude the bundled free PMCP marketplace.",
|
|
7643
|
+
boolean: true
|
|
7644
|
+
},
|
|
7498
7645
|
{
|
|
7499
7646
|
name: "marketplace",
|
|
7500
|
-
describe: "
|
|
7647
|
+
describe: "An installed local Claude, Codex or Grok marketplace directory. Repeatable.",
|
|
7501
7648
|
repeat: true,
|
|
7502
7649
|
placeholder: "<dir>"
|
|
7503
7650
|
},
|
|
@@ -7520,7 +7667,7 @@ var CATALOG_OPTIONS = [
|
|
|
7520
7667
|
},
|
|
7521
7668
|
{
|
|
7522
7669
|
name: "no-config",
|
|
7523
|
-
describe: "Ignore pmcp.toml;
|
|
7670
|
+
describe: "Ignore pmcp.toml; CLI defaults and the given flags apply.",
|
|
7524
7671
|
boolean: true
|
|
7525
7672
|
}
|
|
7526
7673
|
];
|
|
@@ -7552,12 +7699,15 @@ function catalogOptionsFrom(context, onReject) {
|
|
|
7552
7699
|
};
|
|
7553
7700
|
const scopes = pick("scope", config.scopes);
|
|
7554
7701
|
const marketplaces = pick("marketplace", config.marketplaces);
|
|
7702
|
+
const selectedMarketplaces = [...marketplaces];
|
|
7703
|
+
if (!context.args.flags.has("no-builtin") && config.builtin !== false)
|
|
7704
|
+
selectedMarketplaces.push(publicMarketplaceRoot());
|
|
7555
7705
|
const packages = pick("package", config.packages);
|
|
7556
7706
|
const workspaces = pick("workspace", config.workspaces);
|
|
7557
7707
|
return {
|
|
7558
7708
|
roots: rootsFrom(context),
|
|
7559
7709
|
...scopes.length > 0 ? { scopes } : {},
|
|
7560
|
-
...
|
|
7710
|
+
...selectedMarketplaces.length > 0 ? { marketplaces: [...new Set(selectedMarketplaces)] } : {},
|
|
7561
7711
|
...packages.length > 0 ? { packages } : {},
|
|
7562
7712
|
...workspaces.length > 0 ? { workspaces } : {},
|
|
7563
7713
|
...onReject ? { onReject } : {}
|
|
@@ -7629,8 +7779,8 @@ function embeddingFrom(context) {
|
|
|
7629
7779
|
dtype: one(context.args, "dtype") ?? configuration.dtype,
|
|
7630
7780
|
dimensions: dimension2 === void 0 ? configuration.dimensions : Number(dimension2),
|
|
7631
7781
|
revision: one(context.args, "model-revision") ?? configuration.revision,
|
|
7632
|
-
modelPath: modelPath === void 0 ? configuration.modelPath :
|
|
7633
|
-
cacheDir: cacheDir === void 0 ? configuration.cacheDir :
|
|
7782
|
+
modelPath: modelPath === void 0 ? configuration.modelPath : resolve5(context.cwd, modelPath),
|
|
7783
|
+
cacheDir: cacheDir === void 0 ? configuration.cacheDir : resolve5(context.cwd, cacheDir),
|
|
7634
7784
|
encoder: {
|
|
7635
7785
|
...configuration.encoder,
|
|
7636
7786
|
...queryPrefix === void 0 ? {} : { queryPrefix },
|
|
@@ -7691,7 +7841,7 @@ var indexCommand = {
|
|
|
7691
7841
|
);
|
|
7692
7842
|
const force = context.args.flags.has("force");
|
|
7693
7843
|
if (dryRun) {
|
|
7694
|
-
const existing =
|
|
7844
|
+
const existing = existsSync6(path) ? await openEntryVectors(path, { readOnly: true }) : null;
|
|
7695
7845
|
try {
|
|
7696
7846
|
const pending = !force && existing ? existing.stale(selected.indexKey, descriptions) : [...descriptions.keys()];
|
|
7697
7847
|
for (const name of pending) context.ui.line(` would encode ${name}`);
|
|
@@ -7713,7 +7863,7 @@ var indexCommand = {
|
|
|
7713
7863
|
context.ui.info("ranking stays lexical until then, which is the default");
|
|
7714
7864
|
return 3;
|
|
7715
7865
|
}
|
|
7716
|
-
mkdirSync(
|
|
7866
|
+
mkdirSync(dirname5(path), { recursive: true });
|
|
7717
7867
|
const store = await openEntryVectors(path);
|
|
7718
7868
|
if (store === null) {
|
|
7719
7869
|
context.ui.error(
|
|
@@ -7910,7 +8060,7 @@ function registerSkillPrompts(server, tools) {
|
|
|
7910
8060
|
}
|
|
7911
8061
|
|
|
7912
8062
|
// src/resources.ts
|
|
7913
|
-
import { statSync as
|
|
8063
|
+
import { statSync as statSync5 } from "fs";
|
|
7914
8064
|
import {
|
|
7915
8065
|
ResourceNotFoundError,
|
|
7916
8066
|
ResourceTemplate
|
|
@@ -7918,8 +8068,8 @@ import {
|
|
|
7918
8068
|
|
|
7919
8069
|
// src/files/list.ts
|
|
7920
8070
|
import { createHash as createHash2 } from "crypto";
|
|
7921
|
-
import { readFileSync as
|
|
7922
|
-
import { basename as basename4, join as
|
|
8071
|
+
import { readFileSync as readFileSync6, readdirSync as readdirSync3, statSync as statSync2 } from "fs";
|
|
8072
|
+
import { basename as basename4, join as join6, relative as relative3, sep as sep2 } from "path";
|
|
7923
8073
|
|
|
7924
8074
|
// src/files/redact.ts
|
|
7925
8075
|
import { basename as basename3 } from "path";
|
|
@@ -7956,11 +8106,11 @@ function servedBytes(path, raw) {
|
|
|
7956
8106
|
var FILE_LIST_LIMIT = 200;
|
|
7957
8107
|
var sha256 = (bytes) => createHash2("sha256").update(bytes).digest("hex");
|
|
7958
8108
|
function describe(path, full) {
|
|
7959
|
-
const bytes = servedBytes(full,
|
|
8109
|
+
const bytes = servedBytes(full, readFileSync6(full));
|
|
7960
8110
|
return { path, bytes: bytes.length, sha256: sha256(bytes) };
|
|
7961
8111
|
}
|
|
7962
8112
|
function listSkillFiles(root, limit = FILE_LIST_LIMIT) {
|
|
7963
|
-
if (
|
|
8113
|
+
if (statSync2(root).isFile()) {
|
|
7964
8114
|
return { files: [describe(basename4(root), root)], truncated: false };
|
|
7965
8115
|
}
|
|
7966
8116
|
const files = [];
|
|
@@ -7969,14 +8119,14 @@ function listSkillFiles(root, limit = FILE_LIST_LIMIT) {
|
|
|
7969
8119
|
const dir = stack.pop();
|
|
7970
8120
|
for (const entry2 of readdirSync3(dir, { withFileTypes: true })) {
|
|
7971
8121
|
if (entry2.name === "node_modules" || entry2.name === ".git") continue;
|
|
7972
|
-
const full =
|
|
8122
|
+
const full = join6(dir, entry2.name);
|
|
7973
8123
|
if (entry2.isDirectory()) {
|
|
7974
8124
|
stack.push(full);
|
|
7975
8125
|
continue;
|
|
7976
8126
|
}
|
|
7977
8127
|
if (!entry2.isFile()) continue;
|
|
7978
8128
|
if (files.length >= limit) return { files, truncated: true };
|
|
7979
|
-
files.push(describe(
|
|
8129
|
+
files.push(describe(relative3(root, full).split(sep2).join("/"), full));
|
|
7980
8130
|
}
|
|
7981
8131
|
}
|
|
7982
8132
|
files.sort((a, b) => a.path.localeCompare(b.path));
|
|
@@ -7984,7 +8134,7 @@ function listSkillFiles(root, limit = FILE_LIST_LIMIT) {
|
|
|
7984
8134
|
}
|
|
7985
8135
|
|
|
7986
8136
|
// src/files/read.ts
|
|
7987
|
-
import { readFileSync as
|
|
8137
|
+
import { readFileSync as readFileSync7, statSync as statSync4 } from "fs";
|
|
7988
8138
|
|
|
7989
8139
|
// src/mime.ts
|
|
7990
8140
|
var TYPES = {
|
|
@@ -8090,31 +8240,31 @@ function classify(path, bytes) {
|
|
|
8090
8240
|
}
|
|
8091
8241
|
|
|
8092
8242
|
// src/files/resolve.ts
|
|
8093
|
-
import { realpathSync, statSync as
|
|
8094
|
-
import { dirname as
|
|
8095
|
-
var ownFile = (entry2) =>
|
|
8243
|
+
import { realpathSync as realpathSync2, statSync as statSync3 } from "fs";
|
|
8244
|
+
import { dirname as dirname6, isAbsolute as isAbsolute3, relative as relative4, resolve as resolve6, sep as sep3 } from "path";
|
|
8245
|
+
var ownFile = (entry2) => relative4(assetRoot(entry2), entry2.path).split(sep3).join("/");
|
|
8096
8246
|
function resolveSkillFile(entry2, wanted = ownFile(entry2)) {
|
|
8097
|
-
if (
|
|
8247
|
+
if (isAbsolute3(wanted) || wanted.split(/[\\/]/u).includes("..")) {
|
|
8098
8248
|
return { ok: false, error: "path_outside_skill", detail: wanted };
|
|
8099
8249
|
}
|
|
8100
|
-
const scope =
|
|
8101
|
-
const single =
|
|
8102
|
-
const root = single ?
|
|
8250
|
+
const scope = realpathSync2(assetRoot(entry2));
|
|
8251
|
+
const single = statSync3(scope).isFile();
|
|
8252
|
+
const root = single ? dirname6(scope) : scope;
|
|
8103
8253
|
let target2;
|
|
8104
8254
|
try {
|
|
8105
|
-
target2 =
|
|
8106
|
-
single && wanted === "" ? scope :
|
|
8255
|
+
target2 = realpathSync2(
|
|
8256
|
+
single && wanted === "" ? scope : resolve6(root, wanted)
|
|
8107
8257
|
);
|
|
8108
8258
|
} catch {
|
|
8109
8259
|
return { ok: false, error: "no_such_file", detail: wanted };
|
|
8110
8260
|
}
|
|
8111
|
-
const inside = single ? target2 === scope : target2 === root || target2.startsWith(root +
|
|
8261
|
+
const inside = single ? target2 === scope : target2 === root || target2.startsWith(root + sep3);
|
|
8112
8262
|
if (!inside)
|
|
8113
8263
|
return { ok: false, error: "path_outside_skill", detail: wanted };
|
|
8114
8264
|
return {
|
|
8115
8265
|
ok: true,
|
|
8116
8266
|
target: target2,
|
|
8117
|
-
path:
|
|
8267
|
+
path: relative4(root, target2).split(sep3).join("/")
|
|
8118
8268
|
};
|
|
8119
8269
|
}
|
|
8120
8270
|
|
|
@@ -8123,7 +8273,7 @@ var READ_BUDGET_BYTES = 256 * 1024;
|
|
|
8123
8273
|
function readSkillFile(entry2, wanted, budget = READ_BUDGET_BYTES) {
|
|
8124
8274
|
const resolved = resolveSkillFile(entry2, wanted);
|
|
8125
8275
|
if (!resolved.ok) return resolved;
|
|
8126
|
-
const size =
|
|
8276
|
+
const size = statSync4(resolved.target).size;
|
|
8127
8277
|
if (size > budget) {
|
|
8128
8278
|
return {
|
|
8129
8279
|
ok: false,
|
|
@@ -8131,7 +8281,7 @@ function readSkillFile(entry2, wanted, budget = READ_BUDGET_BYTES) {
|
|
|
8131
8281
|
detail: `${size} bytes > ${budget}`
|
|
8132
8282
|
};
|
|
8133
8283
|
}
|
|
8134
|
-
const bytes = servedBytes(resolved.target,
|
|
8284
|
+
const bytes = servedBytes(resolved.target, readFileSync7(resolved.target));
|
|
8135
8285
|
const { kind: kind2, mimeType } = classify(resolved.path, bytes);
|
|
8136
8286
|
return {
|
|
8137
8287
|
ok: true,
|
|
@@ -8171,7 +8321,7 @@ function listing(entries) {
|
|
|
8171
8321
|
name: entry2.name,
|
|
8172
8322
|
description: entry2.description,
|
|
8173
8323
|
mimeType: "text/markdown",
|
|
8174
|
-
size:
|
|
8324
|
+
size: statSync5(entry2.path).size
|
|
8175
8325
|
}));
|
|
8176
8326
|
}
|
|
8177
8327
|
function complete(value, entries) {
|
|
@@ -8208,7 +8358,7 @@ function registerSkillResources(server, skills) {
|
|
|
8208
8358
|
}
|
|
8209
8359
|
|
|
8210
8360
|
// src/server.ts
|
|
8211
|
-
import { readFileSync as
|
|
8361
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
8212
8362
|
|
|
8213
8363
|
// src/find.ts
|
|
8214
8364
|
import { createHash as createHash3 } from "crypto";
|
|
@@ -8297,11 +8447,11 @@ function rank(matches2, limit) {
|
|
|
8297
8447
|
}
|
|
8298
8448
|
|
|
8299
8449
|
// src/files/bundle.ts
|
|
8300
|
-
import { readFileSync as
|
|
8301
|
-
import { dirname as
|
|
8450
|
+
import { readFileSync as readFileSync8, statSync as statSync6 } from "fs";
|
|
8451
|
+
import { dirname as dirname7, join as join7 } from "path";
|
|
8302
8452
|
function bundleSkill(entry2) {
|
|
8303
8453
|
const root = assetRoot(entry2);
|
|
8304
|
-
const base =
|
|
8454
|
+
const base = statSync6(root).isFile() ? dirname7(root) : root;
|
|
8305
8455
|
const listing2 = listSkillFiles(root);
|
|
8306
8456
|
return {
|
|
8307
8457
|
name: entry2.name,
|
|
@@ -8309,7 +8459,7 @@ function bundleSkill(entry2) {
|
|
|
8309
8459
|
files: listing2.files.map((file2) => ({
|
|
8310
8460
|
...file2,
|
|
8311
8461
|
uri: skillFileUri(entry2, file2.path),
|
|
8312
|
-
mimeType: classify(file2.path,
|
|
8462
|
+
mimeType: classify(file2.path, readFileSync8(join7(base, file2.path))).mimeType
|
|
8313
8463
|
}))
|
|
8314
8464
|
};
|
|
8315
8465
|
}
|
|
@@ -8476,7 +8626,7 @@ function createSkillTools(options2) {
|
|
|
8476
8626
|
describe(name) {
|
|
8477
8627
|
const entry2 = byName(name);
|
|
8478
8628
|
if (!entry2) return null;
|
|
8479
|
-
const source2 =
|
|
8629
|
+
const source2 = readFileSync9(entry2.path, "utf8");
|
|
8480
8630
|
const listing2 = listSkillFiles(assetRoot(entry2));
|
|
8481
8631
|
return {
|
|
8482
8632
|
name: entry2.name,
|
|
@@ -8513,7 +8663,7 @@ function createSkillTools(options2) {
|
|
|
8513
8663
|
return {
|
|
8514
8664
|
name: entry2.name,
|
|
8515
8665
|
body: readBody(
|
|
8516
|
-
servedBytes(entry2.path,
|
|
8666
|
+
servedBytes(entry2.path, readFileSync9(entry2.path)).toString("utf8")
|
|
8517
8667
|
),
|
|
8518
8668
|
files: bundle.files.filter((file2) => file2.path !== own),
|
|
8519
8669
|
filesTruncated: bundle.truncated
|
|
@@ -8533,17 +8683,17 @@ function createSkillTools(options2) {
|
|
|
8533
8683
|
|
|
8534
8684
|
// src/extension/entry.ts
|
|
8535
8685
|
import { createHash as createHash4 } from "crypto";
|
|
8536
|
-
import { readFileSync as
|
|
8537
|
-
import { basename as basename5, dirname as
|
|
8686
|
+
import { readFileSync as readFileSync10, readdirSync as readdirSync4, statSync as statSync7 } from "fs";
|
|
8687
|
+
import { basename as basename5, dirname as dirname8, join as join8, relative as relative5, sep as sep4 } from "path";
|
|
8538
8688
|
var MAX_RESOURCES = 512;
|
|
8539
8689
|
var MAX_SKILL_BYTES = 16 * 1024 * 1024;
|
|
8540
8690
|
var digests = /* @__PURE__ */ new Map();
|
|
8541
8691
|
function facts(path) {
|
|
8542
|
-
const stat =
|
|
8692
|
+
const stat = statSync7(path);
|
|
8543
8693
|
const key = `${stat.mtimeMs}:${stat.size}`;
|
|
8544
8694
|
const cached = digests.get(path);
|
|
8545
8695
|
if (cached?.key === key) return cached;
|
|
8546
|
-
const bytes = servedBytes(path,
|
|
8696
|
+
const bytes = servedBytes(path, readFileSync10(path));
|
|
8547
8697
|
const digest2 = `sha256:${createHash4("sha256").update(bytes).digest("hex")}`;
|
|
8548
8698
|
const fresh = { key, digest: digest2, size: bytes.length };
|
|
8549
8699
|
digests.set(path, fresh);
|
|
@@ -8556,7 +8706,7 @@ function filesUnder(root) {
|
|
|
8556
8706
|
const dir = stack.pop();
|
|
8557
8707
|
for (const entry2 of readdirSync4(dir, { withFileTypes: true })) {
|
|
8558
8708
|
if (entry2.name === "node_modules" || entry2.name === ".git") continue;
|
|
8559
|
-
const full =
|
|
8709
|
+
const full = join8(dir, entry2.name);
|
|
8560
8710
|
if (entry2.isDirectory()) stack.push(full);
|
|
8561
8711
|
else if (entry2.isFile()) found.push(full);
|
|
8562
8712
|
if (found.length > MAX_RESOURCES) return null;
|
|
@@ -8567,8 +8717,8 @@ function filesUnder(root) {
|
|
|
8567
8717
|
function extensionEntry(entry2) {
|
|
8568
8718
|
if (kindOf(entry2) !== "skill" || basename5(entry2.path) !== "SKILL.md")
|
|
8569
8719
|
return null;
|
|
8570
|
-
const frontmatter = frontmatterObject(
|
|
8571
|
-
const root =
|
|
8720
|
+
const frontmatter = frontmatterObject(readFileSync10(entry2.path, "utf8"));
|
|
8721
|
+
const root = dirname8(entry2.path);
|
|
8572
8722
|
if (specProblems(frontmatter, basename5(root)).length > 0) return null;
|
|
8573
8723
|
if (frontmatter?.["name"] !== entry2.slug) return null;
|
|
8574
8724
|
const files = filesUnder(root);
|
|
@@ -8577,7 +8727,7 @@ function extensionEntry(entry2) {
|
|
|
8577
8727
|
const resources = files.map((path) => {
|
|
8578
8728
|
const fact = facts(path);
|
|
8579
8729
|
total += fact.size;
|
|
8580
|
-
const file2 =
|
|
8730
|
+
const file2 = relative5(root, path).split(sep4).join("/");
|
|
8581
8731
|
return {
|
|
8582
8732
|
uri: skillFileUri(entry2, file2),
|
|
8583
8733
|
digest: fact.digest,
|
|
@@ -9013,7 +9163,7 @@ function registerMailboxTools(server, service) {
|
|
|
9013
9163
|
|
|
9014
9164
|
// src/mcp.ts
|
|
9015
9165
|
var SERVER_NAME = "pmcp";
|
|
9016
|
-
var SERVER_VERSION = "0.
|
|
9166
|
+
var SERVER_VERSION = "0.14.1";
|
|
9017
9167
|
function registerSkillTools(server, tools) {
|
|
9018
9168
|
registerCatalogTool(server, tools);
|
|
9019
9169
|
registerFindTool(server, tools);
|
|
@@ -9142,7 +9292,7 @@ async function serveWithMailbox(catalog, mailbox, serving = {}) {
|
|
|
9142
9292
|
init_config2();
|
|
9143
9293
|
|
|
9144
9294
|
// src/search.ts
|
|
9145
|
-
import { existsSync as
|
|
9295
|
+
import { existsSync as existsSync9 } from "fs";
|
|
9146
9296
|
init_encoder();
|
|
9147
9297
|
async function prepareSearch(catalog, options2) {
|
|
9148
9298
|
const lexical = (reason) => ({
|
|
@@ -9150,7 +9300,7 @@ async function prepareSearch(catalog, options2) {
|
|
|
9150
9300
|
reason,
|
|
9151
9301
|
options: {}
|
|
9152
9302
|
});
|
|
9153
|
-
if (!
|
|
9303
|
+
if (!existsSync9(options2.indexPath)) return lexical("index_missing");
|
|
9154
9304
|
let store;
|
|
9155
9305
|
try {
|
|
9156
9306
|
store = await openEntryVectors(options2.indexPath, { readOnly: true });
|
|
@@ -9301,16 +9451,16 @@ import {
|
|
|
9301
9451
|
fchmodSync,
|
|
9302
9452
|
mkdirSync as mkdirSync4,
|
|
9303
9453
|
openSync as openSync2,
|
|
9304
|
-
readFileSync as
|
|
9454
|
+
readFileSync as readFileSync12,
|
|
9305
9455
|
rmSync as rmSync2,
|
|
9306
9456
|
writeSync
|
|
9307
9457
|
} from "fs";
|
|
9308
9458
|
import { homedir as homedir2 } from "os";
|
|
9309
|
-
import { join as
|
|
9459
|
+
import { join as join10 } from "path";
|
|
9310
9460
|
var DEVICE_GRANT = "urn:ietf:params:oauth:grant-type:device_code";
|
|
9311
9461
|
function globalDir(home = homedir2(), env = process.env) {
|
|
9312
9462
|
const xdg = env["XDG_CONFIG_HOME"];
|
|
9313
|
-
return xdg !== void 0 && xdg !== "" ?
|
|
9463
|
+
return xdg !== void 0 && xdg !== "" ? join10(xdg, "pmcp") : join10(home, ".pmcp");
|
|
9314
9464
|
}
|
|
9315
9465
|
var form = (fields) => new URLSearchParams(fields).toString();
|
|
9316
9466
|
var FORM_HEADERS = { "content-type": "application/x-www-form-urlencoded" };
|
|
@@ -9420,10 +9570,10 @@ async function pollOnce(metadata2, request3, fetcher2, now = Date.now) {
|
|
|
9420
9570
|
return { kind: "denied" };
|
|
9421
9571
|
}
|
|
9422
9572
|
}
|
|
9423
|
-
var sessionPath = (home) =>
|
|
9573
|
+
var sessionPath = (home) => join10(globalDir(home), "session.json");
|
|
9424
9574
|
function readSession(home) {
|
|
9425
9575
|
try {
|
|
9426
|
-
return JSON.parse(
|
|
9576
|
+
return JSON.parse(readFileSync12(sessionPath(home), "utf8"));
|
|
9427
9577
|
} catch {
|
|
9428
9578
|
return null;
|
|
9429
9579
|
}
|
|
@@ -9477,7 +9627,7 @@ var DEFAULT_RESOURCE = "https://api.pmcp.build";
|
|
|
9477
9627
|
var DEFAULT_SCOPE = "pmcp:catalog:read pmcp:distribution:read";
|
|
9478
9628
|
var fetcher = (url, init) => fetch(url, init);
|
|
9479
9629
|
var setting = (env, name, fallback) => env[name] ?? fallback;
|
|
9480
|
-
var wait = (ms) => new Promise((
|
|
9630
|
+
var wait = (ms) => new Promise((resolve22) => setTimeout(resolve22, ms));
|
|
9481
9631
|
var loginCommand = {
|
|
9482
9632
|
name: "login",
|
|
9483
9633
|
describe: "Sign in on this machine, to obtain skills this project lacks",
|
|
@@ -9645,29 +9795,41 @@ function sayStanding(context, line) {
|
|
|
9645
9795
|
}
|
|
9646
9796
|
|
|
9647
9797
|
// src/installed.ts
|
|
9648
|
-
import { readFileSync as
|
|
9649
|
-
import { dirname as
|
|
9650
|
-
function
|
|
9798
|
+
import { existsSync as existsSync10, readFileSync as readFileSync13 } from "fs";
|
|
9799
|
+
import { dirname as dirname11, join as join11, resolve as resolve8 } from "path";
|
|
9800
|
+
function object2(value) {
|
|
9651
9801
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9652
9802
|
}
|
|
9653
9803
|
function absent(error) {
|
|
9654
|
-
return
|
|
9804
|
+
return object2(error) && (error.code === "ENOENT" || error.code === "ENOTDIR");
|
|
9655
9805
|
}
|
|
9656
9806
|
function packageName(name) {
|
|
9657
9807
|
return /^(?:@[a-z0-9._-]+\/)?[a-z0-9][a-z0-9._-]*$/u.test(name);
|
|
9658
9808
|
}
|
|
9809
|
+
function installedPackageDirectory(project2, name) {
|
|
9810
|
+
if (!packageName(name)) throw new Error("Invalid installed package name");
|
|
9811
|
+
let directory = resolve8(project2);
|
|
9812
|
+
for (; ; ) {
|
|
9813
|
+
const candidate2 = join11(directory, "node_modules", name);
|
|
9814
|
+
if (existsSync10(join11(candidate2, "package.json"))) return candidate2;
|
|
9815
|
+
const parent = dirname11(directory);
|
|
9816
|
+
if (parent === directory)
|
|
9817
|
+
throw new Error("Installed package manifest was not found");
|
|
9818
|
+
directory = parent;
|
|
9819
|
+
}
|
|
9820
|
+
}
|
|
9659
9821
|
function locate(name, project2) {
|
|
9660
9822
|
let directory = project2;
|
|
9661
9823
|
for (; ; ) {
|
|
9662
9824
|
let source2;
|
|
9663
9825
|
try {
|
|
9664
|
-
source2 =
|
|
9665
|
-
|
|
9826
|
+
source2 = readFileSync13(
|
|
9827
|
+
join11(directory, "node_modules", name, "package.json"),
|
|
9666
9828
|
"utf8"
|
|
9667
9829
|
);
|
|
9668
9830
|
} catch (error) {
|
|
9669
9831
|
if (!absent(error)) return { name, reason: "unreadable_manifest" };
|
|
9670
|
-
const parent =
|
|
9832
|
+
const parent = dirname11(directory);
|
|
9671
9833
|
if (parent === directory) return { name, reason: "not_installed" };
|
|
9672
9834
|
directory = parent;
|
|
9673
9835
|
continue;
|
|
@@ -9678,7 +9840,7 @@ function locate(name, project2) {
|
|
|
9678
9840
|
} catch {
|
|
9679
9841
|
return { name, reason: "invalid_manifest" };
|
|
9680
9842
|
}
|
|
9681
|
-
if (!
|
|
9843
|
+
if (!object2(manifest2) || typeof manifest2.name !== "string" || !packageName(manifest2.name) || typeof manifest2.version !== "string" || manifest2.version.trim() === "") {
|
|
9682
9844
|
return { name, reason: "invalid_manifest" };
|
|
9683
9845
|
}
|
|
9684
9846
|
return {
|
|
@@ -9689,11 +9851,11 @@ function locate(name, project2) {
|
|
|
9689
9851
|
}
|
|
9690
9852
|
}
|
|
9691
9853
|
function readInstalledDependencies(projectDirectory) {
|
|
9692
|
-
const project2 =
|
|
9854
|
+
const project2 = resolve8(projectDirectory);
|
|
9693
9855
|
const manifest2 = JSON.parse(
|
|
9694
|
-
|
|
9856
|
+
readFileSync13(join11(project2, "package.json"), "utf8")
|
|
9695
9857
|
);
|
|
9696
|
-
if (!
|
|
9858
|
+
if (!object2(manifest2))
|
|
9697
9859
|
throw new Error("project package.json must contain an object");
|
|
9698
9860
|
const names = /* @__PURE__ */ new Set();
|
|
9699
9861
|
for (const field of [
|
|
@@ -9704,7 +9866,7 @@ function readInstalledDependencies(projectDirectory) {
|
|
|
9704
9866
|
]) {
|
|
9705
9867
|
const declared = manifest2[field];
|
|
9706
9868
|
if (declared === void 0) continue;
|
|
9707
|
-
if (!
|
|
9869
|
+
if (!object2(declared)) throw new Error(`${field} must contain an object`);
|
|
9708
9870
|
for (const [name, range] of Object.entries(declared)) {
|
|
9709
9871
|
if (!packageName(name) || typeof range !== "string") {
|
|
9710
9872
|
throw new Error(`invalid dependency declaration in ${field}`);
|
|
@@ -9724,8 +9886,8 @@ function readInstalledDependencies(projectDirectory) {
|
|
|
9724
9886
|
|
|
9725
9887
|
// src/install/plan.ts
|
|
9726
9888
|
import { createHash as createHash7 } from "crypto";
|
|
9727
|
-
import { existsSync as
|
|
9728
|
-
import { dirname as
|
|
9889
|
+
import { existsSync as existsSync11, readFileSync as readFileSync14 } from "fs";
|
|
9890
|
+
import { dirname as dirname12, join as join12, relative as relative6, resolve as resolve9, sep as sep5 } from "path";
|
|
9729
9891
|
import semver from "semver";
|
|
9730
9892
|
function matchingSkills(catalog, inventory) {
|
|
9731
9893
|
const matches2 = [];
|
|
@@ -9759,7 +9921,7 @@ function fingerprint(files) {
|
|
|
9759
9921
|
for (const file2 of files) {
|
|
9760
9922
|
hash2.update(file2).update("\0");
|
|
9761
9923
|
try {
|
|
9762
|
-
hash2.update(
|
|
9924
|
+
hash2.update(readFileSync14(file2));
|
|
9763
9925
|
} catch (error) {
|
|
9764
9926
|
if (error.code !== "ENOENT") throw error;
|
|
9765
9927
|
hash2.update("absent");
|
|
@@ -9770,32 +9932,32 @@ function fingerprint(files) {
|
|
|
9770
9932
|
}
|
|
9771
9933
|
function manifestAt(directory) {
|
|
9772
9934
|
const value = JSON.parse(
|
|
9773
|
-
|
|
9935
|
+
readFileSync14(join12(directory, "package.json"), "utf8")
|
|
9774
9936
|
);
|
|
9775
9937
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
9776
9938
|
throw new Error("invalid project manifest");
|
|
9777
9939
|
return value;
|
|
9778
9940
|
}
|
|
9779
9941
|
function installContext(directory) {
|
|
9780
|
-
const project2 =
|
|
9942
|
+
const project2 = resolve9(directory);
|
|
9781
9943
|
const manifest2 = manifestAt(project2);
|
|
9782
9944
|
let root = project2;
|
|
9783
9945
|
let ancestor = project2;
|
|
9784
9946
|
for (; ; ) {
|
|
9785
|
-
if (ancestor !== project2 &&
|
|
9947
|
+
if (ancestor !== project2 && existsSync11(join12(ancestor, "package.json"))) {
|
|
9786
9948
|
const candidate2 = manifestAt(ancestor);
|
|
9787
9949
|
if (candidate2.workspaces !== void 0) {
|
|
9788
9950
|
root = ancestor;
|
|
9789
9951
|
break;
|
|
9790
9952
|
}
|
|
9791
|
-
if (LOCKS.some((file2) =>
|
|
9953
|
+
if (LOCKS.some((file2) => existsSync11(join12(ancestor, file2)))) break;
|
|
9792
9954
|
}
|
|
9793
|
-
if (
|
|
9955
|
+
if (existsSync11(join12(ancestor, ".git")) || dirname12(ancestor) === ancestor)
|
|
9794
9956
|
break;
|
|
9795
|
-
ancestor =
|
|
9957
|
+
ancestor = dirname12(ancestor);
|
|
9796
9958
|
}
|
|
9797
9959
|
const owner = manifestAt(root);
|
|
9798
|
-
const lockNames = LOCKS.filter((file2) =>
|
|
9960
|
+
const lockNames = LOCKS.filter((file2) => existsSync11(join12(root, file2)));
|
|
9799
9961
|
const lockManagers = new Set(
|
|
9800
9962
|
lockNames.map((file2) => {
|
|
9801
9963
|
if (file2.startsWith("bun.")) return "bun";
|
|
@@ -9819,16 +9981,16 @@ function installContext(directory) {
|
|
|
9819
9981
|
throw new Error(
|
|
9820
9982
|
"declare packageManager or install project dependencies first"
|
|
9821
9983
|
);
|
|
9822
|
-
if (root !== project2 && LOCKS.some((file2) =>
|
|
9984
|
+
if (root !== project2 && LOCKS.some((file2) => existsSync11(join12(project2, file2)))) {
|
|
9823
9985
|
throw new Error(
|
|
9824
9986
|
"nested project has its own lockfile; resolve the workspace boundary first"
|
|
9825
9987
|
);
|
|
9826
9988
|
}
|
|
9827
9989
|
const files = [
|
|
9828
9990
|
.../* @__PURE__ */ new Set([
|
|
9829
|
-
|
|
9830
|
-
|
|
9831
|
-
...LOCKS.map((file2) =>
|
|
9991
|
+
join12(root, "package.json"),
|
|
9992
|
+
join12(project2, "package.json"),
|
|
9993
|
+
...LOCKS.map((file2) => join12(root, file2))
|
|
9832
9994
|
])
|
|
9833
9995
|
];
|
|
9834
9996
|
return {
|
|
@@ -9916,7 +10078,7 @@ function planInstall(options2) {
|
|
|
9916
10078
|
];
|
|
9917
10079
|
if (context.project !== context.root)
|
|
9918
10080
|
args.push(
|
|
9919
|
-
`--workspace=${
|
|
10081
|
+
`--workspace=${relative6(context.root, context.project).split(sep5).join("/")}`
|
|
9920
10082
|
);
|
|
9921
10083
|
} else {
|
|
9922
10084
|
args = ["add", "--dev", "--exact", "--ignore-scripts"];
|
|
@@ -9936,16 +10098,17 @@ function planInstall(options2) {
|
|
|
9936
10098
|
}
|
|
9937
10099
|
|
|
9938
10100
|
// src/commands/remote-options.ts
|
|
9939
|
-
import { resolve as
|
|
10101
|
+
import { join as join14, resolve as resolve10 } from "path";
|
|
10102
|
+
import { readFileSync as readFileSync17 } from "fs";
|
|
9940
10103
|
|
|
9941
10104
|
// src/remote/catalog.ts
|
|
9942
|
-
import { readFileSync as
|
|
10105
|
+
import { readFileSync as readFileSync16, statSync as statSync8 } from "fs";
|
|
9943
10106
|
import semver2 from "semver";
|
|
9944
10107
|
import { z as z13 } from "zod";
|
|
9945
10108
|
|
|
9946
10109
|
// src/remote/catalog-cache.ts
|
|
9947
|
-
import { mkdirSync as mkdirSync5, readFileSync as
|
|
9948
|
-
import { join as
|
|
10110
|
+
import { mkdirSync as mkdirSync5, readFileSync as readFileSync15, renameSync as renameSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
10111
|
+
import { join as join13 } from "path";
|
|
9949
10112
|
var MAX_CACHE_BYTES = 8 * 1024 * 1024;
|
|
9950
10113
|
function memoryCatalogCache() {
|
|
9951
10114
|
const held = /* @__PURE__ */ new Map();
|
|
@@ -9957,11 +10120,11 @@ function memoryCatalogCache() {
|
|
|
9957
10120
|
};
|
|
9958
10121
|
}
|
|
9959
10122
|
function fileCatalogCache(dir) {
|
|
9960
|
-
const path =
|
|
10123
|
+
const path = join13(dir, "catalog-cache.json");
|
|
9961
10124
|
return {
|
|
9962
10125
|
read(origin) {
|
|
9963
10126
|
try {
|
|
9964
|
-
const raw =
|
|
10127
|
+
const raw = readFileSync15(path, "utf8");
|
|
9965
10128
|
if (raw.length > MAX_CACHE_BYTES) return null;
|
|
9966
10129
|
const held = JSON.parse(raw);
|
|
9967
10130
|
if (typeof held !== "object" || held === null) return null;
|
|
@@ -10081,10 +10244,10 @@ function catalogEndpoint(origin) {
|
|
|
10081
10244
|
return new URL("/v1/catalog", url).href;
|
|
10082
10245
|
}
|
|
10083
10246
|
function readCatalogFile(path) {
|
|
10084
|
-
const stat =
|
|
10247
|
+
const stat = statSync8(path);
|
|
10085
10248
|
if (!stat.isFile() || stat.size > MAX_CATALOG_BYTES)
|
|
10086
10249
|
throw new Error("catalog file exceeds the supported size");
|
|
10087
|
-
const bytes =
|
|
10250
|
+
const bytes = readFileSync16(path);
|
|
10088
10251
|
if (bytes.byteLength > MAX_CATALOG_BYTES)
|
|
10089
10252
|
throw new Error("catalog file exceeds the supported size");
|
|
10090
10253
|
return parseCatalog(JSON.parse(bytes.toString("utf8")));
|
|
@@ -10155,6 +10318,11 @@ async function request(origin, fetcher2, held, cache) {
|
|
|
10155
10318
|
|
|
10156
10319
|
// src/commands/remote-options.ts
|
|
10157
10320
|
var REMOTE_OPTIONS = [
|
|
10321
|
+
{
|
|
10322
|
+
name: "provider",
|
|
10323
|
+
describe: "Public free catalog (default) or explicitly hosted service.",
|
|
10324
|
+
placeholder: "<public|hosted>"
|
|
10325
|
+
},
|
|
10158
10326
|
{
|
|
10159
10327
|
name: "project",
|
|
10160
10328
|
describe: "Project directory containing package.json.",
|
|
@@ -10168,18 +10336,41 @@ var REMOTE_OPTIONS = [
|
|
|
10168
10336
|
},
|
|
10169
10337
|
{ name: "json", describe: "Print machine-readable results.", boolean: true }
|
|
10170
10338
|
];
|
|
10339
|
+
function catalogProvider(context) {
|
|
10340
|
+
const given = one(context.args, "provider");
|
|
10341
|
+
if (given !== void 0 && given !== "public" && given !== "hosted")
|
|
10342
|
+
throw new ArgumentError("Choose --provider public or hosted");
|
|
10343
|
+
const givenApi = one(context.args, "api");
|
|
10344
|
+
const api2 = givenApi ?? context.env.PMCP_API_ORIGIN;
|
|
10345
|
+
if (given === "public" && givenApi)
|
|
10346
|
+
throw new ArgumentError(
|
|
10347
|
+
"The public provider does not accept a hosted API origin"
|
|
10348
|
+
);
|
|
10349
|
+
return given ?? (api2 ? "hosted" : "public");
|
|
10350
|
+
}
|
|
10171
10351
|
function projectFrom(context) {
|
|
10172
|
-
return
|
|
10352
|
+
return resolve10(context.cwd, one(context.args, "project") ?? ".");
|
|
10173
10353
|
}
|
|
10174
10354
|
function apiOrigin(context) {
|
|
10175
10355
|
return one(context.args, "api") ?? context.env["PMCP_API_ORIGIN"] ?? DEFAULT_API_ORIGIN;
|
|
10176
10356
|
}
|
|
10177
10357
|
async function remoteCatalog(context) {
|
|
10358
|
+
const provider = catalogProvider(context);
|
|
10178
10359
|
const file2 = one(context.args, "catalog");
|
|
10179
10360
|
if (file2) {
|
|
10180
10361
|
if (one(context.args, "api"))
|
|
10181
10362
|
throw new Error("choose --catalog or --api, not both");
|
|
10182
|
-
return readCatalogFile(
|
|
10363
|
+
return readCatalogFile(resolve10(context.cwd, file2));
|
|
10364
|
+
}
|
|
10365
|
+
if (provider === "public") {
|
|
10366
|
+
const catalog = JSON.parse(
|
|
10367
|
+
readFileSync17(join14(publicMarketplaceRoot(), "docs/catalog.json"), "utf8")
|
|
10368
|
+
);
|
|
10369
|
+
return parseCatalog({
|
|
10370
|
+
schemaVersion: 1,
|
|
10371
|
+
requestId: "bundled-public",
|
|
10372
|
+
catalog
|
|
10373
|
+
});
|
|
10183
10374
|
}
|
|
10184
10375
|
return fetchCatalog(apiOrigin(context), fetch, fileCatalogCache(globalDir()));
|
|
10185
10376
|
}
|
|
@@ -10222,7 +10413,7 @@ var availableCommand = {
|
|
|
10222
10413
|
if (matches2.length > 0) {
|
|
10223
10414
|
context.ui.info(
|
|
10224
10415
|
"preview",
|
|
10225
|
-
`${invocation()} preview <package> shows
|
|
10416
|
+
`${invocation()} preview <package> shows the contents and a complete example`
|
|
10226
10417
|
);
|
|
10227
10418
|
}
|
|
10228
10419
|
for (const issue of inventory.issues)
|
|
@@ -10235,7 +10426,7 @@ var availableCommand = {
|
|
|
10235
10426
|
// src/commands/preview.ts
|
|
10236
10427
|
var previewCommand = {
|
|
10237
10428
|
name: "preview",
|
|
10238
|
-
describe: "Show
|
|
10429
|
+
describe: "Show a skill's contents and a complete example",
|
|
10239
10430
|
usage: "pmcp preview <skill-package> [--json]",
|
|
10240
10431
|
options: REMOTE_OPTIONS,
|
|
10241
10432
|
async run(context) {
|
|
@@ -10260,8 +10451,6 @@ var previewCommand = {
|
|
|
10260
10451
|
summary: entry2.summary,
|
|
10261
10452
|
package: `${entry2.delivery.packageName}@${entry2.delivery.version}`,
|
|
10262
10453
|
evidence: entry2.evidence,
|
|
10263
|
-
// A script deciding on our behalf needs the recall as much as a
|
|
10264
|
-
// person does, and more so: it will not read the prose.
|
|
10265
10454
|
line: entry2.line,
|
|
10266
10455
|
targets: entry2.targets,
|
|
10267
10456
|
preview: entry2.preview
|
|
@@ -10374,11 +10563,11 @@ function applyInstallPlan(plan2, runner = packageManagerRunner) {
|
|
|
10374
10563
|
if (fingerprint(plan2.context.files) !== plan2.context.fingerprint)
|
|
10375
10564
|
throw new Error("project changed after planning; create a new plan");
|
|
10376
10565
|
for (const entry2 of plan2.changes) {
|
|
10377
|
-
const
|
|
10566
|
+
const integrity2 = runner.integrity(
|
|
10378
10567
|
`${entry2.packageName}@${entry2.version}`,
|
|
10379
10568
|
plan2
|
|
10380
10569
|
);
|
|
10381
|
-
if (
|
|
10570
|
+
if (integrity2 !== entry2.integrity)
|
|
10382
10571
|
throw new Error(
|
|
10383
10572
|
"registry integrity differs from the catalog; nothing was installed"
|
|
10384
10573
|
);
|
|
@@ -10403,8 +10592,8 @@ function applyInstallPlan(plan2, runner = packageManagerRunner) {
|
|
|
10403
10592
|
|
|
10404
10593
|
// src/install/verify-content.ts
|
|
10405
10594
|
import { createHash as createHash8 } from "crypto";
|
|
10406
|
-
import { readdirSync as readdirSync5, readFileSync as
|
|
10407
|
-
import { join as
|
|
10595
|
+
import { readdirSync as readdirSync5, readFileSync as readFileSync18, statSync as statSync9 } from "fs";
|
|
10596
|
+
import { join as join15, relative as relative7, resolve as resolve11, sep as sep6 } from "path";
|
|
10408
10597
|
var SKILL = "SKILL.md";
|
|
10409
10598
|
var SKIP = /* @__PURE__ */ new Set(["node_modules", ".git", ".cache"]);
|
|
10410
10599
|
function skillFiles2(root) {
|
|
@@ -10418,18 +10607,18 @@ function skillFiles2(root) {
|
|
|
10418
10607
|
}
|
|
10419
10608
|
for (const name of entries) {
|
|
10420
10609
|
if (SKIP.has(name)) continue;
|
|
10421
|
-
const path =
|
|
10610
|
+
const path = join15(dir, name);
|
|
10422
10611
|
let stats;
|
|
10423
10612
|
try {
|
|
10424
|
-
stats =
|
|
10613
|
+
stats = statSync9(path);
|
|
10425
10614
|
} catch {
|
|
10426
10615
|
continue;
|
|
10427
10616
|
}
|
|
10428
10617
|
if (stats.isDirectory()) walk(path);
|
|
10429
10618
|
else if (name === SKILL)
|
|
10430
10619
|
found.set(
|
|
10431
|
-
|
|
10432
|
-
|
|
10620
|
+
relative7(root, path).split(sep6).join("/"),
|
|
10621
|
+
readFileSync18(path, "utf8")
|
|
10433
10622
|
);
|
|
10434
10623
|
}
|
|
10435
10624
|
};
|
|
@@ -10445,7 +10634,12 @@ function contentDigest(files) {
|
|
|
10445
10634
|
return `sha256:${hash2.digest("hex")}`;
|
|
10446
10635
|
}
|
|
10447
10636
|
function verifyInstalledContent(project2, packageName2, expected) {
|
|
10448
|
-
|
|
10637
|
+
let root = resolve11(project2, "node_modules", ...packageName2.split("/"));
|
|
10638
|
+
try {
|
|
10639
|
+
root = installedPackageDirectory(project2, packageName2);
|
|
10640
|
+
} catch {
|
|
10641
|
+
root = resolve11(project2, "node_modules", ...packageName2.split("/"));
|
|
10642
|
+
}
|
|
10449
10643
|
const files = skillFiles2(root);
|
|
10450
10644
|
const actual = contentDigest(files);
|
|
10451
10645
|
return {
|
|
@@ -10457,10 +10651,199 @@ function verifyInstalledContent(project2, packageName2, expected) {
|
|
|
10457
10651
|
};
|
|
10458
10652
|
}
|
|
10459
10653
|
|
|
10654
|
+
// src/install/public.ts
|
|
10655
|
+
import { lstatSync, readFileSync as readFileSync19, readdirSync as readdirSync6 } from "fs";
|
|
10656
|
+
import { join as join16 } from "path";
|
|
10657
|
+
|
|
10658
|
+
// src/install/archive.ts
|
|
10659
|
+
import { createHash as createHash9 } from "crypto";
|
|
10660
|
+
import { gzipSync, gunzipSync } from "zlib";
|
|
10661
|
+
var maximumSize = 32 * 1024 * 1024;
|
|
10662
|
+
function integrity(bytes) {
|
|
10663
|
+
return `sha512-${createHash9("sha512").update(bytes).digest("base64")}`;
|
|
10664
|
+
}
|
|
10665
|
+
function safePath(path) {
|
|
10666
|
+
if (!/^package\//u.test(path) || path.includes("\\") || path.split("/").some((part) => !part || part === "." || part === ".."))
|
|
10667
|
+
throw new Error("Unsafe archive path");
|
|
10668
|
+
}
|
|
10669
|
+
function readArchive(bytes) {
|
|
10670
|
+
const tar = gunzipSync(bytes, { maxOutputLength: maximumSize });
|
|
10671
|
+
const files = /* @__PURE__ */ new Map();
|
|
10672
|
+
let offset = 0;
|
|
10673
|
+
while (offset + 512 <= tar.length) {
|
|
10674
|
+
const header = tar.subarray(offset, offset + 512);
|
|
10675
|
+
if (header.every((value) => value === 0)) break;
|
|
10676
|
+
const string = (start2, length) => header.subarray(start2, start2 + length).toString("utf8").replace(/\0.*$/su, "");
|
|
10677
|
+
const name = string(0, 100);
|
|
10678
|
+
const prefix = string(345, 155);
|
|
10679
|
+
const path = prefix ? `${prefix}/${name}` : name;
|
|
10680
|
+
safePath(path);
|
|
10681
|
+
if (header[156] !== 0 && header[156] !== 48)
|
|
10682
|
+
throw new Error("Archive contains a non-regular file");
|
|
10683
|
+
const expected = Number.parseInt(string(148, 8).trim(), 8);
|
|
10684
|
+
const actual = header.reduce(
|
|
10685
|
+
(sum, byte, index) => sum + (index >= 148 && index < 156 ? 32 : byte),
|
|
10686
|
+
0
|
|
10687
|
+
);
|
|
10688
|
+
if (expected !== actual)
|
|
10689
|
+
throw new Error("Archive header checksum mismatch");
|
|
10690
|
+
const size = Number.parseInt(string(124, 12).trim(), 8);
|
|
10691
|
+
if (!Number.isSafeInteger(size) || size < 0 || offset + 512 + size > tar.length)
|
|
10692
|
+
throw new Error("Archive member is truncated");
|
|
10693
|
+
if (files.has(path)) throw new Error("Archive contains duplicate members");
|
|
10694
|
+
files.set(path, tar.subarray(offset + 512, offset + 512 + size));
|
|
10695
|
+
offset += 512 + Math.ceil(size / 512) * 512;
|
|
10696
|
+
}
|
|
10697
|
+
if (!files.size) throw new Error("Archive contains no files");
|
|
10698
|
+
return files;
|
|
10699
|
+
}
|
|
10700
|
+
|
|
10701
|
+
// src/install/public.ts
|
|
10702
|
+
var MAX_ARCHIVE_BYTES = 8 * 1024 * 1024;
|
|
10703
|
+
function publicArchiveUrl(entry2) {
|
|
10704
|
+
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/u.test(entry2.productId) || entry2.line === void 0)
|
|
10705
|
+
throw new Error(
|
|
10706
|
+
"The public catalog requires a product identifier and major line"
|
|
10707
|
+
);
|
|
10708
|
+
return `https://pmcp.build/skills/${entry2.productId}/${entry2.line.major}/releases/${entry2.delivery.version}.tgz`;
|
|
10709
|
+
}
|
|
10710
|
+
function publicEntry(catalog, change) {
|
|
10711
|
+
const entry2 = catalog.entries.find(
|
|
10712
|
+
(item) => item.delivery.packageName === change.packageName && item.delivery.version === change.version
|
|
10713
|
+
);
|
|
10714
|
+
if (!entry2 || entry2.delivery.integrity !== change.integrity)
|
|
10715
|
+
throw new Error("The public delivery is absent from the catalog");
|
|
10716
|
+
return entry2;
|
|
10717
|
+
}
|
|
10718
|
+
function publicInstallPlan(plan2, catalog) {
|
|
10719
|
+
const specs = new Map(
|
|
10720
|
+
plan2.changes.map((change) => [
|
|
10721
|
+
`${change.packageName}@${change.version}`,
|
|
10722
|
+
`${change.packageName}@${publicArchiveUrl(publicEntry(catalog, change))}`
|
|
10723
|
+
])
|
|
10724
|
+
);
|
|
10725
|
+
return {
|
|
10726
|
+
...plan2,
|
|
10727
|
+
command: {
|
|
10728
|
+
...plan2.command,
|
|
10729
|
+
args: plan2.command.args.map(
|
|
10730
|
+
(argument) => specs.get(argument) ?? argument
|
|
10731
|
+
)
|
|
10732
|
+
}
|
|
10733
|
+
};
|
|
10734
|
+
}
|
|
10735
|
+
function publicArchiveFiles(entry2, bytes) {
|
|
10736
|
+
if (bytes.length > MAX_ARCHIVE_BYTES || integrity(bytes) !== entry2.delivery.integrity)
|
|
10737
|
+
throw new Error("Public archive integrity differs from the catalog");
|
|
10738
|
+
const files = readArchive(bytes);
|
|
10739
|
+
const manifest2 = JSON.parse(
|
|
10740
|
+
files.get("package/package.json")?.toString("utf8") ?? "null"
|
|
10741
|
+
);
|
|
10742
|
+
if (!manifest2 || typeof manifest2 !== "object" || Array.isArray(manifest2))
|
|
10743
|
+
throw new Error("Public archive has no package manifest");
|
|
10744
|
+
const metadata2 = manifest2;
|
|
10745
|
+
if (metadata2.name !== entry2.delivery.packageName || metadata2.version !== entry2.delivery.version)
|
|
10746
|
+
throw new Error("Public archive identity differs from the catalog");
|
|
10747
|
+
for (const key of [
|
|
10748
|
+
"scripts",
|
|
10749
|
+
"dependencies",
|
|
10750
|
+
"optionalDependencies",
|
|
10751
|
+
"peerDependencies",
|
|
10752
|
+
"bundledDependencies",
|
|
10753
|
+
"bundleDependencies",
|
|
10754
|
+
"bin",
|
|
10755
|
+
"workspaces"
|
|
10756
|
+
])
|
|
10757
|
+
if (metadata2[key] !== void 0)
|
|
10758
|
+
throw new Error("Public skill packages must be content-only");
|
|
10759
|
+
const skills = /* @__PURE__ */ new Map();
|
|
10760
|
+
if (!files.has("package/LICENSE"))
|
|
10761
|
+
throw new Error("Public skill packages must include their license");
|
|
10762
|
+
for (const [path, content] of files) {
|
|
10763
|
+
if (path === "package/package.json" || path === "package/LICENSE") continue;
|
|
10764
|
+
if (!/^package\/skills\/[a-z0-9]+(?:-[a-z0-9]+)*\/(?:SKILL\.md|references\/.+)$/u.test(
|
|
10765
|
+
path
|
|
10766
|
+
))
|
|
10767
|
+
throw new Error("Public archive contains an unexpected resource");
|
|
10768
|
+
if (path.endsWith("/SKILL.md"))
|
|
10769
|
+
skills.set(path.slice("package/".length), content.toString("utf8"));
|
|
10770
|
+
}
|
|
10771
|
+
if (skills.size !== 1 || contentDigest(skills) !== entry2.contentDigest)
|
|
10772
|
+
throw new Error("Public skill content differs from the catalog");
|
|
10773
|
+
return files;
|
|
10774
|
+
}
|
|
10775
|
+
async function downloadArchive(url, fetcher2) {
|
|
10776
|
+
const response = await fetcher2(url, {
|
|
10777
|
+
redirect: "error",
|
|
10778
|
+
signal: AbortSignal.timeout(15e3)
|
|
10779
|
+
});
|
|
10780
|
+
if (!response.ok || !response.body)
|
|
10781
|
+
throw new Error(`Public archive request failed (${response.status})`);
|
|
10782
|
+
const reader = response.body.getReader();
|
|
10783
|
+
const chunks = [];
|
|
10784
|
+
let size = 0;
|
|
10785
|
+
try {
|
|
10786
|
+
for (; ; ) {
|
|
10787
|
+
const { done, value } = await reader.read();
|
|
10788
|
+
if (done) break;
|
|
10789
|
+
size += value.byteLength;
|
|
10790
|
+
if (size > MAX_ARCHIVE_BYTES)
|
|
10791
|
+
throw new Error("Public archive exceeds the download budget");
|
|
10792
|
+
chunks.push(value);
|
|
10793
|
+
}
|
|
10794
|
+
} finally {
|
|
10795
|
+
await reader.cancel().catch(() => void 0);
|
|
10796
|
+
reader.releaseLock();
|
|
10797
|
+
}
|
|
10798
|
+
return Buffer.concat(chunks);
|
|
10799
|
+
}
|
|
10800
|
+
async function preparePublicDelivery(plan2, catalog, fetcher2 = fetch) {
|
|
10801
|
+
const verified = /* @__PURE__ */ new Map();
|
|
10802
|
+
const resources = /* @__PURE__ */ new Map();
|
|
10803
|
+
for (const change of plan2.changes) {
|
|
10804
|
+
const entry2 = publicEntry(catalog, change);
|
|
10805
|
+
const url = publicArchiveUrl(entry2);
|
|
10806
|
+
const bytes = await downloadArchive(url, fetcher2);
|
|
10807
|
+
resources.set(change.packageName, publicArchiveFiles(entry2, bytes));
|
|
10808
|
+
const identity = `${change.packageName}@${change.version}`;
|
|
10809
|
+
verified.set(identity, integrity(bytes));
|
|
10810
|
+
}
|
|
10811
|
+
return { integrity: verified, resources };
|
|
10812
|
+
}
|
|
10813
|
+
function verifyPublicDelivery(project2, delivery) {
|
|
10814
|
+
for (const [name, files] of delivery.resources) {
|
|
10815
|
+
const root = installedPackageDirectory(project2, name);
|
|
10816
|
+
const expected = new Set(
|
|
10817
|
+
[...files.keys()].map((path) => path.slice("package/".length))
|
|
10818
|
+
);
|
|
10819
|
+
const walk = (directory, prefix = "") => {
|
|
10820
|
+
for (const entry2 of readdirSync6(directory, { withFileTypes: true })) {
|
|
10821
|
+
const path = prefix ? `${prefix}/${entry2.name}` : entry2.name;
|
|
10822
|
+
if (entry2.isDirectory()) {
|
|
10823
|
+
walk(join16(directory, entry2.name), path);
|
|
10824
|
+
continue;
|
|
10825
|
+
}
|
|
10826
|
+
if (!entry2.isFile() || !expected.has(path))
|
|
10827
|
+
throw new Error(
|
|
10828
|
+
"Installed public package contains unexpected resources"
|
|
10829
|
+
);
|
|
10830
|
+
}
|
|
10831
|
+
};
|
|
10832
|
+
walk(root);
|
|
10833
|
+
for (const [path, bytes] of files) {
|
|
10834
|
+
const destination = join16(root, path.slice("package/".length));
|
|
10835
|
+
if (!lstatSync(destination).isFile() || !readFileSync19(destination).equals(bytes))
|
|
10836
|
+
throw new Error(
|
|
10837
|
+
"Installed public package differs from its verified archive; inspect the project before retrying"
|
|
10838
|
+
);
|
|
10839
|
+
}
|
|
10840
|
+
}
|
|
10841
|
+
}
|
|
10842
|
+
|
|
10460
10843
|
// src/remote/distribution.ts
|
|
10461
10844
|
import { chmodSync as chmodSync4, mkdtempSync, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
10462
10845
|
import { tmpdir } from "os";
|
|
10463
|
-
import { join as
|
|
10846
|
+
import { join as join17 } from "path";
|
|
10464
10847
|
function api(origin, path) {
|
|
10465
10848
|
const base = new URL(catalogEndpoint(origin));
|
|
10466
10849
|
base.pathname = path;
|
|
@@ -10600,9 +10983,9 @@ async function revokeDistributionCredential(accessToken, id2, origin = DEFAULT_A
|
|
|
10600
10983
|
}
|
|
10601
10984
|
}
|
|
10602
10985
|
function temporaryRegistryConfig(value) {
|
|
10603
|
-
const directory = mkdtempSync(
|
|
10986
|
+
const directory = mkdtempSync(join17(tmpdir(), "pmcp-registry-"));
|
|
10604
10987
|
chmodSync4(directory, 448);
|
|
10605
|
-
const path =
|
|
10988
|
+
const path = join17(directory, ".npmrc");
|
|
10606
10989
|
const registry = new URL(value.registry);
|
|
10607
10990
|
const auth = `//${registry.host}${registry.pathname}:_authToken=${value.token}`;
|
|
10608
10991
|
writeFileSync3(path, `${value.scope}:registry=${registry.href}
|
|
@@ -10632,10 +11015,11 @@ async function manage(context, sync) {
|
|
|
10632
11015
|
throw new ArgumentError("select a skill package or pass --all");
|
|
10633
11016
|
}
|
|
10634
11017
|
const project2 = projectFrom(context);
|
|
11018
|
+
const provider = catalogProvider(context);
|
|
10635
11019
|
const install = installContext(project2);
|
|
10636
11020
|
const inventory = readInstalledDependencies(project2);
|
|
10637
11021
|
const catalog = await remoteCatalog(context);
|
|
10638
|
-
const
|
|
11022
|
+
const selectedPlan = planInstall({
|
|
10639
11023
|
context: install,
|
|
10640
11024
|
inventory,
|
|
10641
11025
|
catalog,
|
|
@@ -10643,6 +11027,7 @@ async function manage(context, sync) {
|
|
|
10643
11027
|
all: context.args.flags.has("all"),
|
|
10644
11028
|
sync
|
|
10645
11029
|
});
|
|
11030
|
+
const plan2 = provider === "public" ? publicInstallPlan(selectedPlan, catalog) : selectedPlan;
|
|
10646
11031
|
const digests2 = new Map(
|
|
10647
11032
|
catalog.entries.map((entry2) => [
|
|
10648
11033
|
entry2.delivery.packageName,
|
|
@@ -10728,6 +11113,32 @@ async function manage(context, sync) {
|
|
|
10728
11113
|
prompt.close();
|
|
10729
11114
|
}
|
|
10730
11115
|
}
|
|
11116
|
+
if (provider === "public") {
|
|
11117
|
+
const delivery = await preparePublicDelivery(plan2, catalog);
|
|
11118
|
+
const code = applyInstallPlan(
|
|
11119
|
+
plan2,
|
|
11120
|
+
createPackageManagerRunner(void 0, process.env, delivery.integrity)
|
|
11121
|
+
);
|
|
11122
|
+
if (code !== 0) {
|
|
11123
|
+
context.ui.error(
|
|
11124
|
+
"package manager failed",
|
|
11125
|
+
`exit ${code}; inspect the project before retrying`
|
|
11126
|
+
);
|
|
11127
|
+
return code;
|
|
11128
|
+
}
|
|
11129
|
+
verifyPublicDelivery(project2, delivery);
|
|
11130
|
+
if (context.args.flags.has("json")) {
|
|
11131
|
+
context.ui.data(
|
|
11132
|
+
`${JSON.stringify({ status: "applied", ...report }, null, 2)}
|
|
11133
|
+
`
|
|
11134
|
+
);
|
|
11135
|
+
return 0;
|
|
11136
|
+
}
|
|
11137
|
+
context.ui.success(
|
|
11138
|
+
`${plan2.changes.length} public skill packages installed`
|
|
11139
|
+
);
|
|
11140
|
+
return 0;
|
|
11141
|
+
}
|
|
10731
11142
|
const session = readSession();
|
|
10732
11143
|
if (!session || session.expiresAt !== void 0 && session.expiresAt <= Date.now()) {
|
|
10733
11144
|
throw new Error(
|
|
@@ -10825,38 +11236,38 @@ var syncCommand = {
|
|
|
10825
11236
|
import { homedir as homedir6 } from "os";
|
|
10826
11237
|
|
|
10827
11238
|
// src/project/apply.ts
|
|
10828
|
-
import { createHash as
|
|
11239
|
+
import { createHash as createHash13 } from "crypto";
|
|
10829
11240
|
import {
|
|
10830
|
-
existsSync as
|
|
10831
|
-
lstatSync as
|
|
11241
|
+
existsSync as existsSync17,
|
|
11242
|
+
lstatSync as lstatSync4,
|
|
10832
11243
|
mkdirSync as mkdirSync7,
|
|
10833
|
-
readFileSync as
|
|
10834
|
-
readdirSync as
|
|
11244
|
+
readFileSync as readFileSync24,
|
|
11245
|
+
readdirSync as readdirSync9,
|
|
10835
11246
|
readlinkSync as readlinkSync2,
|
|
10836
11247
|
rmSync as rmSync4,
|
|
10837
11248
|
symlinkSync,
|
|
10838
11249
|
writeFileSync as writeFileSync5
|
|
10839
11250
|
} from "fs";
|
|
10840
|
-
import { dirname as
|
|
11251
|
+
import { dirname as dirname16, join as join22, posix as posix2 } from "path";
|
|
10841
11252
|
import { parse as parse4 } from "smol-toml";
|
|
10842
11253
|
|
|
10843
11254
|
// src/project/backup.ts
|
|
10844
|
-
import { createHash as
|
|
11255
|
+
import { createHash as createHash10 } from "crypto";
|
|
10845
11256
|
import {
|
|
10846
|
-
existsSync as
|
|
10847
|
-
lstatSync as
|
|
11257
|
+
existsSync as existsSync13,
|
|
11258
|
+
lstatSync as lstatSync3,
|
|
10848
11259
|
mkdirSync as mkdirSync6,
|
|
10849
11260
|
mkdtempSync as mkdtempSync2,
|
|
10850
|
-
readFileSync as
|
|
11261
|
+
readFileSync as readFileSync20,
|
|
10851
11262
|
writeFileSync as writeFileSync4
|
|
10852
11263
|
} from "fs";
|
|
10853
11264
|
import { homedir as homedir3 } from "os";
|
|
10854
|
-
import { dirname as
|
|
11265
|
+
import { dirname as dirname14, isAbsolute as isAbsolute4, join as join19, relative as relative8, resolve as resolve12 } from "path";
|
|
10855
11266
|
|
|
10856
11267
|
// src/project/package-rules.ts
|
|
10857
11268
|
import { execFileSync } from "child_process";
|
|
10858
|
-
import { existsSync as
|
|
10859
|
-
import { dirname as
|
|
11269
|
+
import { existsSync as existsSync12, lstatSync as lstatSync2, readdirSync as readdirSync7, readlinkSync } from "fs";
|
|
11270
|
+
import { dirname as dirname13, join as join18, posix } from "path";
|
|
10860
11271
|
var ProjectError = class extends Error {
|
|
10861
11272
|
constructor(message) {
|
|
10862
11273
|
super(message);
|
|
@@ -10893,7 +11304,7 @@ function deriveName(dir, strip) {
|
|
|
10893
11304
|
}
|
|
10894
11305
|
function linkTarget(abs) {
|
|
10895
11306
|
try {
|
|
10896
|
-
if (!
|
|
11307
|
+
if (!lstatSync2(abs).isSymbolicLink()) return null;
|
|
10897
11308
|
return readlinkSync(abs);
|
|
10898
11309
|
} catch {
|
|
10899
11310
|
return null;
|
|
@@ -10901,11 +11312,11 @@ function linkTarget(abs) {
|
|
|
10901
11312
|
}
|
|
10902
11313
|
function legacyLinks(root) {
|
|
10903
11314
|
const found = /* @__PURE__ */ new Map();
|
|
10904
|
-
const dir =
|
|
10905
|
-
if (!
|
|
10906
|
-
for (const entry2 of
|
|
11315
|
+
const dir = join18(root, RULES_DIR);
|
|
11316
|
+
if (!existsSync12(dir)) return found;
|
|
11317
|
+
for (const entry2 of readdirSync7(dir).sort()) {
|
|
10907
11318
|
if (!entry2.endsWith(".md")) continue;
|
|
10908
|
-
const target2 = linkTarget(
|
|
11319
|
+
const target2 = linkTarget(join18(dir, entry2));
|
|
10909
11320
|
if (target2 === null || !target2.startsWith("../../")) continue;
|
|
10910
11321
|
found.set(entry2.slice(0, -3), target2.slice("../../".length));
|
|
10911
11322
|
}
|
|
@@ -10925,14 +11336,14 @@ function packageRuleOutputs(root, spec, owned) {
|
|
|
10925
11336
|
const legacyByDir = /* @__PURE__ */ new Map();
|
|
10926
11337
|
for (const [name, target2] of legacy) {
|
|
10927
11338
|
if (!byTarget.has(target2)) byTarget.set(target2, name);
|
|
10928
|
-
const dir =
|
|
11339
|
+
const dir = dirname13(target2);
|
|
10929
11340
|
if (!legacyByDir.has(dir)) legacyByDir.set(dir, name);
|
|
10930
11341
|
}
|
|
10931
11342
|
const outputs = [];
|
|
10932
11343
|
const taken = /* @__PURE__ */ new Map();
|
|
10933
11344
|
for (const file2 of files) {
|
|
10934
11345
|
const pkg = file2.slice(0, -suffix.length);
|
|
10935
|
-
const name = byTarget.get(file2) ?? legacyByDir.get(
|
|
11346
|
+
const name = byTarget.get(file2) ?? legacyByDir.get(dirname13(file2)) ?? deriveName(pkg, spec.strip);
|
|
10936
11347
|
const other = taken.get(name);
|
|
10937
11348
|
if (other !== void 0) {
|
|
10938
11349
|
throw new ProjectError(
|
|
@@ -10951,13 +11362,13 @@ function packageRuleOutputs(root, spec, owned) {
|
|
|
10951
11362
|
|
|
10952
11363
|
// src/project/backup.ts
|
|
10953
11364
|
function backupFiles(root, paths) {
|
|
10954
|
-
const base =
|
|
10955
|
-
const inside =
|
|
10956
|
-
if (!
|
|
11365
|
+
const base = join19(homedir3(), ".cache/pmcp/project-backups");
|
|
11366
|
+
const inside = relative8(resolve12(root), resolve12(base));
|
|
11367
|
+
if (!isAbsolute4(inside) && inside !== ".." && !inside.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`)) {
|
|
10957
11368
|
throw new ProjectError("Project backups must be outside the project");
|
|
10958
11369
|
}
|
|
10959
11370
|
mkdirSync6(base, { recursive: true, mode: 448 });
|
|
10960
|
-
const directory =
|
|
11371
|
+
const directory = lstatSync3(base);
|
|
10961
11372
|
if (directory.isSymbolicLink() || !directory.isDirectory()) {
|
|
10962
11373
|
throw new ProjectError("Project backup directory must be a real directory");
|
|
10963
11374
|
}
|
|
@@ -10967,42 +11378,42 @@ function backupFiles(root, paths) {
|
|
|
10967
11378
|
);
|
|
10968
11379
|
}
|
|
10969
11380
|
const snapshots = [...new Set(paths)].map((path) => {
|
|
10970
|
-
const absolute2 =
|
|
10971
|
-
const within =
|
|
10972
|
-
if (
|
|
11381
|
+
const absolute2 = resolve12(root, path);
|
|
11382
|
+
const within = relative8(resolve12(root), absolute2);
|
|
11383
|
+
if (isAbsolute4(within) || within === ".." || within.startsWith("../") || within.startsWith("..\\")) {
|
|
10973
11384
|
throw new ProjectError("Backup paths must stay inside the project");
|
|
10974
11385
|
}
|
|
10975
|
-
if (!
|
|
11386
|
+
if (!existsSync13(absolute2)) {
|
|
10976
11387
|
return { path, contents: null };
|
|
10977
11388
|
}
|
|
10978
|
-
const stat =
|
|
11389
|
+
const stat = lstatSync3(absolute2);
|
|
10979
11390
|
if (!stat.isFile() || stat.isSymbolicLink()) {
|
|
10980
11391
|
throw new ProjectError(
|
|
10981
11392
|
`Refusing to back up a nonregular config file: ${path}`
|
|
10982
11393
|
);
|
|
10983
11394
|
}
|
|
10984
|
-
return { path, contents:
|
|
11395
|
+
return { path, contents: readFileSync20(absolute2) };
|
|
10985
11396
|
});
|
|
10986
|
-
const destination = mkdtempSync2(
|
|
11397
|
+
const destination = mkdtempSync2(join19(base, "project-"));
|
|
10987
11398
|
const entries = snapshots.map(({ path, contents }) => {
|
|
10988
11399
|
if (contents === null) {
|
|
10989
11400
|
return { path, present: false };
|
|
10990
11401
|
}
|
|
10991
|
-
const output =
|
|
10992
|
-
mkdirSync6(
|
|
11402
|
+
const output = join19(destination, "files", path);
|
|
11403
|
+
mkdirSync6(dirname14(output), { recursive: true, mode: 448 });
|
|
10993
11404
|
writeFileSync4(output, contents, { mode: 384 });
|
|
10994
11405
|
return {
|
|
10995
11406
|
path,
|
|
10996
11407
|
present: true,
|
|
10997
|
-
sha256:
|
|
11408
|
+
sha256: createHash10("sha256").update(contents).digest("hex")
|
|
10998
11409
|
};
|
|
10999
11410
|
});
|
|
11000
11411
|
writeFileSync4(
|
|
11001
|
-
|
|
11412
|
+
join19(destination, "receipt.json"),
|
|
11002
11413
|
JSON.stringify(
|
|
11003
11414
|
{
|
|
11004
11415
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11005
|
-
projectRoot:
|
|
11416
|
+
projectRoot: resolve12(root),
|
|
11006
11417
|
entries
|
|
11007
11418
|
},
|
|
11008
11419
|
null,
|
|
@@ -11028,7 +11439,7 @@ function splitKeyed(path) {
|
|
|
11028
11439
|
}
|
|
11029
11440
|
|
|
11030
11441
|
// src/project/mcp.ts
|
|
11031
|
-
import { isAbsolute as
|
|
11442
|
+
import { isAbsolute as isAbsolute5, relative as relative9 } from "path";
|
|
11032
11443
|
var runtimeByTool = {
|
|
11033
11444
|
claude: "claude-code",
|
|
11034
11445
|
codex: "codex-cli",
|
|
@@ -11179,12 +11590,12 @@ function nativeEntry(spec, alias, server, tool) {
|
|
|
11179
11590
|
}
|
|
11180
11591
|
let started;
|
|
11181
11592
|
if (Object.keys(server.envFrom ?? {}).length > 0) {
|
|
11182
|
-
const entry2 =
|
|
11593
|
+
const entry2 = relative9(spec.root, process.argv[1] ?? "");
|
|
11183
11594
|
const driverEnv = {};
|
|
11184
11595
|
if (server.mailbox) {
|
|
11185
11596
|
driverEnv["PMCP_MAILBOX_RUNTIME"] = runtimeByTool[tool];
|
|
11186
11597
|
}
|
|
11187
|
-
if (!
|
|
11598
|
+
if (!isAbsolute5(entry2) && !entry2.startsWith("..")) {
|
|
11188
11599
|
started = launch({
|
|
11189
11600
|
command: "node",
|
|
11190
11601
|
args: [`${ROOT_VAR}/${entry2}`, "project", "exec-mcp", alias],
|
|
@@ -11292,16 +11703,16 @@ function mcpOutputs(spec) {
|
|
|
11292
11703
|
}
|
|
11293
11704
|
|
|
11294
11705
|
// src/project/hooks.ts
|
|
11295
|
-
import { createHash as
|
|
11296
|
-
import { existsSync as
|
|
11297
|
-
import { join as
|
|
11298
|
-
import { fileURLToPath } from "url";
|
|
11706
|
+
import { createHash as createHash12 } from "crypto";
|
|
11707
|
+
import { existsSync as existsSync15, readFileSync as readFileSync22 } from "fs";
|
|
11708
|
+
import { join as join20 } from "path";
|
|
11709
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
11299
11710
|
|
|
11300
11711
|
// src/project/hook-adapter.ts
|
|
11301
11712
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
11302
|
-
import { createHash as
|
|
11303
|
-
import { existsSync as
|
|
11304
|
-
import { dirname as
|
|
11713
|
+
import { createHash as createHash11 } from "crypto";
|
|
11714
|
+
import { existsSync as existsSync14, readFileSync as readFileSync21, realpathSync as realpathSync3, statSync as statSync10 } from "fs";
|
|
11715
|
+
import { dirname as dirname15, isAbsolute as isAbsolute6, relative as relative10, resolve as resolve13 } from "path";
|
|
11305
11716
|
var denied = () => ({
|
|
11306
11717
|
decision: "deny",
|
|
11307
11718
|
reason: "Shell guard failed validation or execution"
|
|
@@ -11311,22 +11722,22 @@ function guardPath(root, command2) {
|
|
|
11311
11722
|
if (!script?.startsWith(".agents/guards/")) {
|
|
11312
11723
|
throw new Error("Hook command must execute a script under .agents/guards/");
|
|
11313
11724
|
}
|
|
11314
|
-
const absolute2 =
|
|
11315
|
-
const base =
|
|
11316
|
-
const inside =
|
|
11317
|
-
if (
|
|
11725
|
+
const absolute2 = resolve13(root, script);
|
|
11726
|
+
const base = resolve13(root, ".agents/guards");
|
|
11727
|
+
const inside = relative10(base, absolute2);
|
|
11728
|
+
if (isAbsolute6(inside) || inside === ".." || inside.startsWith("../") || inside.startsWith("..\\")) {
|
|
11318
11729
|
throw new Error("Hook guard must stay under .agents/guards/");
|
|
11319
11730
|
}
|
|
11320
|
-
if (!
|
|
11731
|
+
if (!existsSync14(absolute2) || !statSync10(absolute2).isFile()) {
|
|
11321
11732
|
throw new Error("Hook guard script must exist");
|
|
11322
11733
|
}
|
|
11323
|
-
const actual =
|
|
11324
|
-
if (
|
|
11734
|
+
const actual = relative10(base, realpathSync3(absolute2));
|
|
11735
|
+
if (isAbsolute6(actual) || actual === ".." || actual.startsWith("../") || actual.startsWith("..\\")) {
|
|
11325
11736
|
throw new Error("Hook guard cannot resolve outside .agents/guards/");
|
|
11326
11737
|
}
|
|
11327
11738
|
return absolute2;
|
|
11328
11739
|
}
|
|
11329
|
-
function
|
|
11740
|
+
function object3(value) {
|
|
11330
11741
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
11331
11742
|
return {};
|
|
11332
11743
|
}
|
|
@@ -11350,9 +11761,9 @@ function scalar(value) {
|
|
|
11350
11761
|
}
|
|
11351
11762
|
function evaluateShellHook(root, policy, tool, payload) {
|
|
11352
11763
|
try {
|
|
11353
|
-
const value =
|
|
11354
|
-
const call =
|
|
11355
|
-
const input = tool === "antigravity" ?
|
|
11764
|
+
const value = object3(payload);
|
|
11765
|
+
const call = object3(value["toolCall"]);
|
|
11766
|
+
const input = tool === "antigravity" ? object3(call["args"]) : object3(value["tool_input"]);
|
|
11356
11767
|
const name = tool === "antigravity" ? call["name"] : value["tool_name"];
|
|
11357
11768
|
const shellNames = {
|
|
11358
11769
|
codex: ["Bash", "exec_command", "shell", "shell_command"],
|
|
@@ -11400,7 +11811,7 @@ function evaluateShellHook(root, policy, tool, payload) {
|
|
|
11400
11811
|
if (result.status !== 0 || result.error) {
|
|
11401
11812
|
return denied();
|
|
11402
11813
|
}
|
|
11403
|
-
const response =
|
|
11814
|
+
const response = object3(JSON.parse(result.stdout));
|
|
11404
11815
|
if (Object.keys(response).some(
|
|
11405
11816
|
(key) => !["decision", "reason"].includes(key)
|
|
11406
11817
|
) || !["allow", "deny"].includes(String(response["decision"])) || response["reason"] !== void 0 && typeof response["reason"] !== "string") {
|
|
@@ -11427,7 +11838,7 @@ function nativeHookDecision(tool, decision) {
|
|
|
11427
11838
|
return decision;
|
|
11428
11839
|
}
|
|
11429
11840
|
async function main() {
|
|
11430
|
-
const [, , , tool, file2,
|
|
11841
|
+
const [, , , tool, file2, integrity2] = process.argv;
|
|
11431
11842
|
const supported = tool === "codex" || tool === "gemini" || tool === "antigravity";
|
|
11432
11843
|
if (!supported) {
|
|
11433
11844
|
process.exitCode = 2;
|
|
@@ -11438,10 +11849,10 @@ async function main() {
|
|
|
11438
11849
|
if (!file2) {
|
|
11439
11850
|
throw new Error("Missing hook policy");
|
|
11440
11851
|
}
|
|
11441
|
-
const bytes =
|
|
11442
|
-
const adapter =
|
|
11443
|
-
const actual =
|
|
11444
|
-
if (actual !==
|
|
11852
|
+
const bytes = readFileSync21(file2);
|
|
11853
|
+
const adapter = readFileSync21(process.argv[1]);
|
|
11854
|
+
const actual = createHash11("sha256").update(adapter).update(bytes).digest("hex");
|
|
11855
|
+
if (actual !== integrity2) {
|
|
11445
11856
|
throw new Error("Hook integrity mismatch");
|
|
11446
11857
|
}
|
|
11447
11858
|
const policy = JSON.parse(bytes.toString("utf8"));
|
|
@@ -11458,8 +11869,8 @@ async function main() {
|
|
|
11458
11869
|
}
|
|
11459
11870
|
chunks.push(bytes2);
|
|
11460
11871
|
}
|
|
11461
|
-
const root =
|
|
11462
|
-
const guardSha256 =
|
|
11872
|
+
const root = resolve13(dirname15(file2), "../..");
|
|
11873
|
+
const guardSha256 = createHash11("sha256").update(readFileSync21(guardPath(root, policy.command))).digest("hex");
|
|
11463
11874
|
if (policy.contractVersion !== 1 || guardSha256 !== policy.guardSha256) {
|
|
11464
11875
|
throw new Error(
|
|
11465
11876
|
"Hook guard source changed; reproject and review native trust"
|
|
@@ -11503,7 +11914,7 @@ var native = {
|
|
|
11503
11914
|
matcher: "run_command"
|
|
11504
11915
|
}
|
|
11505
11916
|
};
|
|
11506
|
-
function
|
|
11917
|
+
function object4(value) {
|
|
11507
11918
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
11508
11919
|
throw new ProjectError(
|
|
11509
11920
|
"Native hook configuration must be an object; contents are redacted"
|
|
@@ -11512,20 +11923,20 @@ function object3(value) {
|
|
|
11512
11923
|
return value;
|
|
11513
11924
|
}
|
|
11514
11925
|
function groups(root, file2, key) {
|
|
11515
|
-
const absolute2 =
|
|
11516
|
-
if (!
|
|
11926
|
+
const absolute2 = join20(root, file2);
|
|
11927
|
+
if (!existsSync15(absolute2)) {
|
|
11517
11928
|
return [];
|
|
11518
11929
|
}
|
|
11519
11930
|
let value;
|
|
11520
11931
|
try {
|
|
11521
|
-
value = JSON.parse(
|
|
11932
|
+
value = JSON.parse(readFileSync22(absolute2, "utf8"));
|
|
11522
11933
|
} catch {
|
|
11523
11934
|
throw new ProjectError(
|
|
11524
11935
|
"Cannot parse native hook configuration; contents are redacted"
|
|
11525
11936
|
);
|
|
11526
11937
|
}
|
|
11527
11938
|
for (const part of key) {
|
|
11528
|
-
value =
|
|
11939
|
+
value = object4(value)[part];
|
|
11529
11940
|
if (value === void 0) {
|
|
11530
11941
|
return [];
|
|
11531
11942
|
}
|
|
@@ -11536,21 +11947,21 @@ function groups(root, file2, key) {
|
|
|
11536
11947
|
return value;
|
|
11537
11948
|
}
|
|
11538
11949
|
function adapterSource() {
|
|
11539
|
-
const published =
|
|
11950
|
+
const published = fileURLToPath2(
|
|
11540
11951
|
new URL("./project/hook-adapter.js", import.meta.url)
|
|
11541
11952
|
);
|
|
11542
|
-
const sourceBuild =
|
|
11953
|
+
const sourceBuild = fileURLToPath2(
|
|
11543
11954
|
new URL("../../dist/project/hook-adapter.js", import.meta.url)
|
|
11544
11955
|
);
|
|
11545
|
-
const file2 =
|
|
11546
|
-
if (!
|
|
11956
|
+
const file2 = existsSync15(published) ? published : sourceBuild;
|
|
11957
|
+
if (!existsSync15(file2)) {
|
|
11547
11958
|
throw new ProjectError("Build pmcp before projecting shell hook adapters");
|
|
11548
11959
|
}
|
|
11549
|
-
return
|
|
11960
|
+
return readFileSync22(file2, "utf8");
|
|
11550
11961
|
}
|
|
11551
11962
|
function hookOutputs(spec) {
|
|
11552
|
-
const lockFile =
|
|
11553
|
-
const lock =
|
|
11963
|
+
const lockFile = join20(spec.root, "pmcp.lock");
|
|
11964
|
+
const lock = existsSync15(lockFile) ? JSON.parse(readFileSync22(lockFile, "utf8")) : {};
|
|
11554
11965
|
const previous = new Map(
|
|
11555
11966
|
(lock.outputs ?? []).filter((entry2) => entry2.managed !== void 0).map((entry2) => [entry2.path, entry2.managed])
|
|
11556
11967
|
);
|
|
@@ -11567,17 +11978,17 @@ function hookOutputs(spec) {
|
|
|
11567
11978
|
outputs.push({ kind: "file", path: ADAPTER, content: adapter });
|
|
11568
11979
|
for (const hook of selected) {
|
|
11569
11980
|
const policyFile = `${HOOK_DIRECTORY}/${hook.name}.json`;
|
|
11570
|
-
const guardSha256 =
|
|
11981
|
+
const guardSha256 = createHash12("sha256").update(readFileSync22(guardPath(spec.root, hook.command))).digest("hex");
|
|
11571
11982
|
const policy = `${JSON.stringify({ ...hook, contractVersion: 1, guardSha256 }, null, 2)}
|
|
11572
11983
|
`;
|
|
11573
11984
|
outputs.push({ kind: "file", path: policyFile, content: policy });
|
|
11574
|
-
const
|
|
11985
|
+
const integrity2 = createHash12("sha256").update(adapter).update(policy).digest("hex");
|
|
11575
11986
|
for (const tool of hook.targets) {
|
|
11576
11987
|
if (!spec.tools.has(tool)) {
|
|
11577
11988
|
continue;
|
|
11578
11989
|
}
|
|
11579
11990
|
const target2 = native[tool];
|
|
11580
|
-
const command2 = `d="$PWD"; while [ ! -f "$d/pmcp.toml" ]; do [ "$d" = / ] && exit 1; d=$(dirname "$d"); done; exec node "$d/${ADAPTER}" --pmcp-shell-hook ${tool} "$d/${policyFile}" ${
|
|
11991
|
+
const command2 = `d="$PWD"; while [ ! -f "$d/pmcp.toml" ]; do [ "$d" = / ] && exit 1; d=$(dirname "$d"); done; exec node "$d/${ADAPTER}" --pmcp-shell-hook ${tool} "$d/${policyFile}" ${integrity2}`;
|
|
11581
11992
|
const timeout = tool === "gemini" ? hook.timeoutMs + 2e3 : Math.ceil(hook.timeoutMs / 1e3) + 2;
|
|
11582
11993
|
const value = {
|
|
11583
11994
|
matcher: target2.matcher,
|
|
@@ -11636,8 +12047,8 @@ function splitFrontmatter(source2) {
|
|
|
11636
12047
|
const match = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/u.exec(source2);
|
|
11637
12048
|
if (!match) return { meta: {}, body: source2 };
|
|
11638
12049
|
const meta = parse3(match[1] ?? "");
|
|
11639
|
-
const
|
|
11640
|
-
return { meta:
|
|
12050
|
+
const object9 = meta !== null && typeof meta === "object" && !Array.isArray(meta) ? meta : {};
|
|
12051
|
+
return { meta: object9, body: match[2] ?? "" };
|
|
11641
12052
|
}
|
|
11642
12053
|
function scalar2(value) {
|
|
11643
12054
|
if (typeof value === "string") return value;
|
|
@@ -11665,28 +12076,28 @@ ${lines2.join("\n")}
|
|
|
11665
12076
|
}
|
|
11666
12077
|
|
|
11667
12078
|
// src/project/plan.ts
|
|
11668
|
-
import { existsSync as
|
|
11669
|
-
import { join as
|
|
12079
|
+
import { existsSync as existsSync16, readFileSync as readFileSync23, readdirSync as readdirSync8 } from "fs";
|
|
12080
|
+
import { join as join21 } from "path";
|
|
11670
12081
|
var AGENT_FIELDS = {
|
|
11671
12082
|
claude: ["name", "description", "tools", "model", "effort"],
|
|
11672
12083
|
gemini: ["name", "description"]
|
|
11673
12084
|
};
|
|
11674
12085
|
var SETTINGS = ".claude/settings.json";
|
|
11675
12086
|
function listDirs(dir) {
|
|
11676
|
-
if (!
|
|
11677
|
-
return
|
|
12087
|
+
if (!existsSync16(dir)) return [];
|
|
12088
|
+
return readdirSync8(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort();
|
|
11678
12089
|
}
|
|
11679
12090
|
var tomlString = (value) => JSON.stringify(typeof value === "string" ? value : String(value ?? ""));
|
|
11680
12091
|
function skillNames(root) {
|
|
11681
|
-
const dir =
|
|
12092
|
+
const dir = join21(root, ".agents/skills");
|
|
11682
12093
|
return listDirs(dir).filter(
|
|
11683
|
-
(name) =>
|
|
12094
|
+
(name) => existsSync16(join21(dir, name, "SKILL.md"))
|
|
11684
12095
|
);
|
|
11685
12096
|
}
|
|
11686
12097
|
function agentNames(root) {
|
|
11687
|
-
const dir =
|
|
12098
|
+
const dir = join21(root, ".agents/agents");
|
|
11688
12099
|
return listDirs(dir).filter(
|
|
11689
|
-
(name) =>
|
|
12100
|
+
(name) => existsSync16(join21(dir, name, "agent.md"))
|
|
11690
12101
|
);
|
|
11691
12102
|
}
|
|
11692
12103
|
function skillOutputs(spec) {
|
|
@@ -11720,13 +12131,13 @@ policy:
|
|
|
11720
12131
|
}
|
|
11721
12132
|
function ruleOutputs(spec) {
|
|
11722
12133
|
if (!spec.tools.has("claude")) return [];
|
|
11723
|
-
const dir =
|
|
11724
|
-
if (!
|
|
12134
|
+
const dir = join21(spec.root, ".agents/rules");
|
|
12135
|
+
if (!existsSync16(dir)) return [];
|
|
11725
12136
|
const outputs = [];
|
|
11726
|
-
for (const file2 of
|
|
12137
|
+
for (const file2 of readdirSync8(dir).filter((f) => f.endsWith(".md")).sort()) {
|
|
11727
12138
|
const rel = `.agents/rules/${file2}`;
|
|
11728
12139
|
const { meta, body } = splitFrontmatter(
|
|
11729
|
-
|
|
12140
|
+
readFileSync23(join21(spec.root, rel), "utf8")
|
|
11730
12141
|
);
|
|
11731
12142
|
const globs = [meta["globs"] ?? []].flat().filter((g) => typeof g === "string" && g !== "");
|
|
11732
12143
|
const head = globs.length > 0 ? renderFrontmatter({ paths: globs }) : "";
|
|
@@ -11745,7 +12156,7 @@ function agentOutputs(spec) {
|
|
|
11745
12156
|
for (const name of agentNames(spec.root)) {
|
|
11746
12157
|
const rel = `.agents/agents/${name}/agent.md`;
|
|
11747
12158
|
const { meta, body } = splitFrontmatter(
|
|
11748
|
-
|
|
12159
|
+
readFileSync23(join21(spec.root, rel), "utf8")
|
|
11749
12160
|
);
|
|
11750
12161
|
const overrides = spec.agents[name] ?? {};
|
|
11751
12162
|
const note = `<!-- ${MARK} from ${rel} -->
|
|
@@ -11822,26 +12233,26 @@ var PREDECESSORS = [
|
|
|
11822
12233
|
"by sync-claude-projection.sh",
|
|
11823
12234
|
"AUTO-GENERATED by scripts/agent/project.mjs"
|
|
11824
12235
|
];
|
|
11825
|
-
var sha = (text3) =>
|
|
12236
|
+
var sha = (text3) => createHash13("sha256").update(text3).digest("hex");
|
|
11826
12237
|
function readLock(root) {
|
|
11827
|
-
const path =
|
|
11828
|
-
if (!
|
|
11829
|
-
const lock = JSON.parse(
|
|
12238
|
+
const path = join22(root, LOCK_FILE);
|
|
12239
|
+
if (!existsSync17(path)) return [];
|
|
12240
|
+
const lock = JSON.parse(readFileSync24(path, "utf8"));
|
|
11830
12241
|
return lock.outputs ?? [];
|
|
11831
12242
|
}
|
|
11832
12243
|
function linkOf(abs) {
|
|
11833
12244
|
try {
|
|
11834
|
-
return
|
|
12245
|
+
return lstatSync4(abs).isSymbolicLink() ? readlinkSync2(abs) : null;
|
|
11835
12246
|
} catch {
|
|
11836
12247
|
return null;
|
|
11837
12248
|
}
|
|
11838
12249
|
}
|
|
11839
|
-
var exists = (abs) =>
|
|
12250
|
+
var exists = (abs) => existsSync17(abs) || linkOf(abs) !== null;
|
|
11840
12251
|
function readJson2(abs) {
|
|
11841
|
-
if (!
|
|
12252
|
+
if (!existsSync17(abs)) return {};
|
|
11842
12253
|
let value;
|
|
11843
12254
|
try {
|
|
11844
|
-
value = JSON.parse(
|
|
12255
|
+
value = JSON.parse(readFileSync24(abs, "utf8"));
|
|
11845
12256
|
} catch {
|
|
11846
12257
|
throw new ProjectError(
|
|
11847
12258
|
"Cannot parse native JSON configuration; contents are redacted"
|
|
@@ -11849,16 +12260,16 @@ function readJson2(abs) {
|
|
|
11849
12260
|
}
|
|
11850
12261
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
11851
12262
|
}
|
|
11852
|
-
function getKey(
|
|
11853
|
-
let node =
|
|
12263
|
+
function getKey(object9, key) {
|
|
12264
|
+
let node = object9;
|
|
11854
12265
|
for (const part of key) {
|
|
11855
12266
|
if (node === null || typeof node !== "object") return void 0;
|
|
11856
12267
|
node = node[part];
|
|
11857
12268
|
}
|
|
11858
12269
|
return node;
|
|
11859
12270
|
}
|
|
11860
|
-
function setKey(
|
|
11861
|
-
let node =
|
|
12271
|
+
function setKey(object9, key, value) {
|
|
12272
|
+
let node = object9;
|
|
11862
12273
|
for (const part of key.slice(0, -1)) {
|
|
11863
12274
|
const next = node[part];
|
|
11864
12275
|
if (next === null || typeof next !== "object" || Array.isArray(next)) {
|
|
@@ -11868,17 +12279,17 @@ function setKey(object8, key, value) {
|
|
|
11868
12279
|
}
|
|
11869
12280
|
node[key[key.length - 1] ?? ""] = value;
|
|
11870
12281
|
}
|
|
11871
|
-
function deleteKey(
|
|
12282
|
+
function deleteKey(object9, key) {
|
|
11872
12283
|
const [head, ...rest] = key;
|
|
11873
|
-
if (head === void 0 || !(head in
|
|
12284
|
+
if (head === void 0 || !(head in object9)) return;
|
|
11874
12285
|
if (rest.length === 0) {
|
|
11875
|
-
delete
|
|
12286
|
+
delete object9[head];
|
|
11876
12287
|
return;
|
|
11877
12288
|
}
|
|
11878
|
-
const child =
|
|
12289
|
+
const child = object9[head];
|
|
11879
12290
|
if (child === null || typeof child !== "object") return;
|
|
11880
12291
|
deleteKey(child, rest);
|
|
11881
|
-
if (Object.keys(child).length === 0) delete
|
|
12292
|
+
if (Object.keys(child).length === 0) delete object9[head];
|
|
11882
12293
|
}
|
|
11883
12294
|
var same = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
11884
12295
|
var block = (content) => `${BEGIN}
|
|
@@ -11893,19 +12304,19 @@ function withoutBlock(text3) {
|
|
|
11893
12304
|
return `${text3.slice(0, start2).replace(/\n+$/u, "\n")}${after}`;
|
|
11894
12305
|
}
|
|
11895
12306
|
function present(root, output) {
|
|
11896
|
-
const abs =
|
|
12307
|
+
const abs = join22(root, "file" in output ? output.file : output.path);
|
|
11897
12308
|
switch (output.kind) {
|
|
11898
12309
|
case "link":
|
|
11899
12310
|
return linkOf(abs) === output.target;
|
|
11900
12311
|
case "file":
|
|
11901
|
-
return
|
|
12312
|
+
return existsSync17(abs) && linkOf(abs) === null && readFileSync24(abs, "utf8") === output.content;
|
|
11902
12313
|
case "json":
|
|
11903
12314
|
if (output.managed && Array.isArray(output.value) && output.value.length === 0) {
|
|
11904
12315
|
return getKey(readJson2(abs), output.key) === void 0;
|
|
11905
12316
|
}
|
|
11906
12317
|
return same(getKey(readJson2(abs), output.key), output.value);
|
|
11907
12318
|
case "block":
|
|
11908
|
-
return
|
|
12319
|
+
return existsSync17(abs) && readFileSync24(abs, "utf8").includes(block(output.content));
|
|
11909
12320
|
}
|
|
11910
12321
|
}
|
|
11911
12322
|
function replaceable(root, output, owned) {
|
|
@@ -11913,12 +12324,12 @@ function replaceable(root, output, owned) {
|
|
|
11913
12324
|
if (output.key[0] !== "mcpServers" || owned.has(output.path)) {
|
|
11914
12325
|
return true;
|
|
11915
12326
|
}
|
|
11916
|
-
const existing = getKey(readJson2(
|
|
12327
|
+
const existing = getKey(readJson2(join22(root, output.file)), output.key);
|
|
11917
12328
|
return existing === void 0 || same(existing, output.value);
|
|
11918
12329
|
}
|
|
11919
12330
|
if (output.kind === "block") {
|
|
11920
|
-
const absolute2 =
|
|
11921
|
-
const text4 =
|
|
12331
|
+
const absolute2 = join22(root, output.file);
|
|
12332
|
+
const text4 = existsSync17(absolute2) ? readFileSync24(absolute2, "utf8") : "";
|
|
11922
12333
|
const outside = parse4(withoutBlock(text4));
|
|
11923
12334
|
const proposed = parse4(output.content);
|
|
11924
12335
|
const existing = objectKeys(outside["mcp_servers"]);
|
|
@@ -11927,14 +12338,14 @@ function replaceable(root, output, owned) {
|
|
|
11927
12338
|
);
|
|
11928
12339
|
}
|
|
11929
12340
|
if (owned.has(output.path)) return true;
|
|
11930
|
-
const abs =
|
|
12341
|
+
const abs = join22(root, output.path);
|
|
11931
12342
|
if (!exists(abs)) return true;
|
|
11932
12343
|
const target2 = linkOf(abs);
|
|
11933
12344
|
if (target2 !== null) {
|
|
11934
12345
|
return output.kind === "link" && target2 === output.target;
|
|
11935
12346
|
}
|
|
11936
|
-
if (
|
|
11937
|
-
const text3 =
|
|
12347
|
+
if (lstatSync4(abs).isDirectory()) return false;
|
|
12348
|
+
const text3 = readFileSync24(abs, "utf8");
|
|
11938
12349
|
if (output.kind === "file" && text3 === output.content) return true;
|
|
11939
12350
|
return PREDECESSORS.some((mark) => text3.includes(mark));
|
|
11940
12351
|
}
|
|
@@ -11948,16 +12359,16 @@ function undeclared(spec, outputs) {
|
|
|
11948
12359
|
const declared = new Set(outputs.map((o) => o.path));
|
|
11949
12360
|
const found = [];
|
|
11950
12361
|
for (const dir of censusDirs(spec)) {
|
|
11951
|
-
const abs =
|
|
11952
|
-
if (!
|
|
11953
|
-
for (const entry2 of
|
|
12362
|
+
const abs = join22(spec.root, dir);
|
|
12363
|
+
if (!existsSync17(abs)) continue;
|
|
12364
|
+
for (const entry2 of readdirSync9(abs)) {
|
|
11954
12365
|
const path = `${dir}/${entry2}`;
|
|
11955
12366
|
if (!declared.has(path)) found.push(path);
|
|
11956
12367
|
}
|
|
11957
12368
|
}
|
|
11958
|
-
const rules =
|
|
11959
|
-
if (spec.tools.has("grok") &&
|
|
11960
|
-
for (const entry2 of
|
|
12369
|
+
const rules = join22(spec.root, RULES_DIR);
|
|
12370
|
+
if (spec.tools.has("grok") && existsSync17(rules)) {
|
|
12371
|
+
for (const entry2 of readdirSync9(rules, { withFileTypes: true })) {
|
|
11961
12372
|
if (entry2.isDirectory()) continue;
|
|
11962
12373
|
found.push(`${RULES_DIR}/${entry2.name}`);
|
|
11963
12374
|
}
|
|
@@ -11983,9 +12394,9 @@ function deadGlobs(root, outputs) {
|
|
|
11983
12394
|
if (rule.kind === "file") {
|
|
11984
12395
|
text3 = rule.content;
|
|
11985
12396
|
} else if (rule.kind === "link") {
|
|
11986
|
-
const abs =
|
|
11987
|
-
if (!
|
|
11988
|
-
text3 =
|
|
12397
|
+
const abs = join22(root, posix2.join(posix2.dirname(rule.path), rule.target));
|
|
12398
|
+
if (!existsSync17(abs)) continue;
|
|
12399
|
+
text3 = readFileSync24(abs, "utf8");
|
|
11989
12400
|
} else {
|
|
11990
12401
|
continue;
|
|
11991
12402
|
}
|
|
@@ -12035,7 +12446,7 @@ function project(spec, options2) {
|
|
|
12035
12446
|
}
|
|
12036
12447
|
}
|
|
12037
12448
|
for (const path of jsonPaths) {
|
|
12038
|
-
readJson2(
|
|
12449
|
+
readJson2(join22(spec.root, path));
|
|
12039
12450
|
}
|
|
12040
12451
|
if (options2.check) {
|
|
12041
12452
|
const problems = [
|
|
@@ -12097,8 +12508,8 @@ function project(spec, options2) {
|
|
|
12097
12508
|
for (const output of outputs) {
|
|
12098
12509
|
if (output.kind !== "link" && output.kind !== "file") continue;
|
|
12099
12510
|
if (present(spec.root, output)) continue;
|
|
12100
|
-
const abs =
|
|
12101
|
-
mkdirSync7(
|
|
12511
|
+
const abs = join22(spec.root, output.path);
|
|
12512
|
+
mkdirSync7(dirname16(abs), { recursive: true });
|
|
12102
12513
|
rmSync4(abs, { force: true, recursive: true });
|
|
12103
12514
|
if (output.kind === "link") symlinkSync(output.target, abs);
|
|
12104
12515
|
else writeFileSync5(abs, output.content);
|
|
@@ -12123,12 +12534,12 @@ function project(spec, options2) {
|
|
|
12123
12534
|
group.drops.push(keyed.key);
|
|
12124
12535
|
jsonFiles.set(keyed.file, group);
|
|
12125
12536
|
} else {
|
|
12126
|
-
rmSync4(
|
|
12537
|
+
rmSync4(join22(spec.root, entry2.path), { force: true, recursive: true });
|
|
12127
12538
|
}
|
|
12128
12539
|
pruned += 1;
|
|
12129
12540
|
}
|
|
12130
12541
|
for (const [file2, { sets, drops }] of jsonFiles) {
|
|
12131
|
-
const abs =
|
|
12542
|
+
const abs = join22(spec.root, file2);
|
|
12132
12543
|
const before = readJson2(abs);
|
|
12133
12544
|
const after = structuredClone(before);
|
|
12134
12545
|
for (const key of drops) deleteKey(after, key);
|
|
@@ -12143,21 +12554,21 @@ function project(spec, options2) {
|
|
|
12143
12554
|
}
|
|
12144
12555
|
}
|
|
12145
12556
|
if (same(before, after)) continue;
|
|
12146
|
-
if (!
|
|
12147
|
-
mkdirSync7(
|
|
12557
|
+
if (!existsSync17(abs) && Object.keys(after).length === 0) continue;
|
|
12558
|
+
mkdirSync7(dirname16(abs), { recursive: true });
|
|
12148
12559
|
writeFileSync5(abs, `${JSON.stringify(after, null, 2)}
|
|
12149
12560
|
`);
|
|
12150
12561
|
}
|
|
12151
12562
|
for (const [file2, content] of blockFiles) {
|
|
12152
|
-
const abs =
|
|
12153
|
-
const before =
|
|
12563
|
+
const abs = join22(spec.root, file2);
|
|
12564
|
+
const before = existsSync17(abs) ? readFileSync24(abs, "utf8") : "";
|
|
12154
12565
|
let after = withoutBlock(before);
|
|
12155
12566
|
if (content !== null) {
|
|
12156
|
-
const
|
|
12157
|
-
after = `${after}${
|
|
12567
|
+
const sep7 = after === "" || after.endsWith("\n\n") ? "" : after.endsWith("\n") ? "\n" : "\n\n";
|
|
12568
|
+
after = `${after}${sep7}${block(content)}`;
|
|
12158
12569
|
}
|
|
12159
12570
|
if (after === before) continue;
|
|
12160
|
-
mkdirSync7(
|
|
12571
|
+
mkdirSync7(dirname16(abs), { recursive: true });
|
|
12161
12572
|
writeFileSync5(abs, after);
|
|
12162
12573
|
}
|
|
12163
12574
|
if (spec.packageRules) {
|
|
@@ -12169,15 +12580,15 @@ function project(spec, options2) {
|
|
|
12169
12580
|
)
|
|
12170
12581
|
);
|
|
12171
12582
|
for (const [name, target2] of legacyLinks(spec.root)) {
|
|
12172
|
-
const abs =
|
|
12173
|
-
const broken = !
|
|
12583
|
+
const abs = join22(spec.root, RULES_DIR, `${name}.md`);
|
|
12584
|
+
const broken = !existsSync17(abs);
|
|
12174
12585
|
if (!broken && !linkedDirs.has(posix2.dirname(target2))) continue;
|
|
12175
12586
|
rmSync4(abs, { force: true });
|
|
12176
12587
|
pruned += 1;
|
|
12177
12588
|
}
|
|
12178
12589
|
}
|
|
12179
12590
|
writeFileSync5(
|
|
12180
|
-
|
|
12591
|
+
join22(spec.root, LOCK_FILE),
|
|
12181
12592
|
`${JSON.stringify({ version: 1, outputs: outputs.filter((output) => output.kind !== "json" || output.managed === void 0 || output.managed.length > 0).map(lockEntry) }, null, 2)}
|
|
12182
12593
|
`
|
|
12183
12594
|
);
|
|
@@ -12191,9 +12602,9 @@ function project(spec, options2) {
|
|
|
12191
12602
|
}
|
|
12192
12603
|
|
|
12193
12604
|
// src/project/mcp-import.ts
|
|
12194
|
-
import { readFileSync as
|
|
12195
|
-
import { createHash as
|
|
12196
|
-
import { basename as basename6, dirname as
|
|
12605
|
+
import { readFileSync as readFileSync25, statSync as statSync11, writeFileSync as writeFileSync6 } from "fs";
|
|
12606
|
+
import { createHash as createHash14 } from "crypto";
|
|
12607
|
+
import { basename as basename6, dirname as dirname17, resolve as resolve14 } from "path";
|
|
12197
12608
|
import { homedir as homedir4 } from "os";
|
|
12198
12609
|
import { parse as parse5, stringify } from "smol-toml";
|
|
12199
12610
|
|
|
@@ -12393,7 +12804,7 @@ var KEYS = /* @__PURE__ */ new Set([
|
|
|
12393
12804
|
function fail(path, where, detail) {
|
|
12394
12805
|
throw new ConfigError(path, `${where} ${detail}`);
|
|
12395
12806
|
}
|
|
12396
|
-
function
|
|
12807
|
+
function object5(path, where, value) {
|
|
12397
12808
|
if (value === void 0) {
|
|
12398
12809
|
return {};
|
|
12399
12810
|
}
|
|
@@ -12416,7 +12827,7 @@ function list(path, where, value, unique2 = true) {
|
|
|
12416
12827
|
}
|
|
12417
12828
|
function mapping(path, where, value) {
|
|
12418
12829
|
const result = {};
|
|
12419
|
-
for (const [key, item] of Object.entries(
|
|
12830
|
+
for (const [key, item] of Object.entries(object5(path, where, value))) {
|
|
12420
12831
|
if (typeof item !== "string") {
|
|
12421
12832
|
fail(path, where, "values must be strings");
|
|
12422
12833
|
}
|
|
@@ -12434,14 +12845,14 @@ function nativeOptions(path, where, value) {
|
|
|
12434
12845
|
};
|
|
12435
12846
|
const result = {};
|
|
12436
12847
|
for (const [tool, raw] of Object.entries(
|
|
12437
|
-
|
|
12848
|
+
object5(path, `${where} native`, value)
|
|
12438
12849
|
)) {
|
|
12439
12850
|
if (!TOOLS.includes(tool)) {
|
|
12440
12851
|
fail(path, where, `unknown native target ${tool}`);
|
|
12441
12852
|
}
|
|
12442
12853
|
const values = {};
|
|
12443
12854
|
for (const [key, item] of Object.entries(
|
|
12444
|
-
|
|
12855
|
+
object5(path, `${where} native.${tool}`, raw)
|
|
12445
12856
|
)) {
|
|
12446
12857
|
if (!fields[tool].includes(key) || typeof item !== "number" || !Number.isFinite(item) || item <= 0) {
|
|
12447
12858
|
fail(path, where, `unsupported native option ${tool}.${key}`);
|
|
@@ -12455,12 +12866,12 @@ function nativeOptions(path, where, value) {
|
|
|
12455
12866
|
function environmentSources(path, where, value) {
|
|
12456
12867
|
const result = {};
|
|
12457
12868
|
for (const [key, raw] of Object.entries(
|
|
12458
|
-
|
|
12869
|
+
object5(path, `${where} env_from`, value)
|
|
12459
12870
|
)) {
|
|
12460
12871
|
if (!ENV_NAME.test(key) || key === "PROJECT_ROOT") {
|
|
12461
12872
|
fail(path, where, "invalid environment source name");
|
|
12462
12873
|
}
|
|
12463
|
-
const source2 =
|
|
12874
|
+
const source2 = object5(path, `${where} env_from.${key}`, raw);
|
|
12464
12875
|
if (Object.keys(source2).some((name) => name !== "file" && name !== "pointer")) {
|
|
12465
12876
|
fail(path, where, "environment sources accept only file and pointer");
|
|
12466
12877
|
}
|
|
@@ -12473,12 +12884,12 @@ function environmentSources(path, where, value) {
|
|
|
12473
12884
|
}
|
|
12474
12885
|
function readMcp(path, value) {
|
|
12475
12886
|
const servers = {};
|
|
12476
|
-
for (const [alias, raw] of Object.entries(
|
|
12887
|
+
for (const [alias, raw] of Object.entries(object5(path, "[mcp]", value))) {
|
|
12477
12888
|
const where = `[mcp.${alias}]`;
|
|
12478
12889
|
if (!/^[a-z0-9][a-z0-9-]*$/u.test(alias)) {
|
|
12479
12890
|
fail(path, where, "use lowercase, digits and -");
|
|
12480
12891
|
}
|
|
12481
|
-
const entry2 =
|
|
12892
|
+
const entry2 = object5(path, where, raw);
|
|
12482
12893
|
for (const key of Object.keys(entry2)) {
|
|
12483
12894
|
if (!KEYS.has(key)) {
|
|
12484
12895
|
fail(path, where, `unknown field ${key}`);
|
|
@@ -12607,7 +13018,7 @@ function readMcp(path, value) {
|
|
|
12607
13018
|
}
|
|
12608
13019
|
|
|
12609
13020
|
// src/project/mcp-import.ts
|
|
12610
|
-
function
|
|
13021
|
+
function object6(value) {
|
|
12611
13022
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
12612
13023
|
throw new ProjectError(
|
|
12613
13024
|
"MCP source must contain object-valued server entries"
|
|
@@ -12623,12 +13034,12 @@ function sourceServers(source2, root) {
|
|
|
12623
13034
|
"Use --source <claude|codex|gemini|grok|antigravity>:<file>"
|
|
12624
13035
|
);
|
|
12625
13036
|
}
|
|
12626
|
-
const path =
|
|
12627
|
-
if (
|
|
13037
|
+
const path = resolve14(root, source2.slice(split + 1));
|
|
13038
|
+
if (statSync11(path).size > 2 * 1024 * 1024) {
|
|
12628
13039
|
throw new ProjectError("MCP source exceeds the 2 MiB limit");
|
|
12629
13040
|
}
|
|
12630
|
-
const bytes =
|
|
12631
|
-
const sha2562 =
|
|
13041
|
+
const bytes = readFileSync25(path);
|
|
13042
|
+
const sha2562 = createHash14("sha256").update(bytes).digest("hex");
|
|
12632
13043
|
const text3 = bytes.toString("utf8").replace(/^\uFEFF/u, "");
|
|
12633
13044
|
let parsed;
|
|
12634
13045
|
try {
|
|
@@ -12637,7 +13048,7 @@ function sourceServers(source2, root) {
|
|
|
12637
13048
|
} else if (path.endsWith(".toml")) {
|
|
12638
13049
|
parsed = parse5(text3);
|
|
12639
13050
|
} else {
|
|
12640
|
-
parsed =
|
|
13051
|
+
parsed = object6(JSON.parse(text3));
|
|
12641
13052
|
}
|
|
12642
13053
|
} catch {
|
|
12643
13054
|
throw new ProjectError(
|
|
@@ -12645,7 +13056,7 @@ function sourceServers(source2, root) {
|
|
|
12645
13056
|
);
|
|
12646
13057
|
}
|
|
12647
13058
|
const servers = parsed["mcpServers"] ?? parsed["mcp_servers"] ?? {};
|
|
12648
|
-
return { tool, path, servers:
|
|
13059
|
+
return { tool, path, servers: object6(servers), sha256: sha2562 };
|
|
12649
13060
|
}
|
|
12650
13061
|
function httpCandidate(tool, entry2) {
|
|
12651
13062
|
if (tool === "gemini" && entry2["url"] !== void 0) {
|
|
@@ -12667,7 +13078,7 @@ function httpCandidate(tool, entry2) {
|
|
|
12667
13078
|
const result = { url };
|
|
12668
13079
|
const headers = {};
|
|
12669
13080
|
const headerEnv = {};
|
|
12670
|
-
const sourceHeaders =
|
|
13081
|
+
const sourceHeaders = object6(entry2["headers"] ?? entry2["http_headers"] ?? {});
|
|
12671
13082
|
for (const [key, value] of Object.entries(sourceHeaders)) {
|
|
12672
13083
|
if (typeof value !== "string") {
|
|
12673
13084
|
throw new ProjectError("HTTP header values must be strings");
|
|
@@ -12687,7 +13098,7 @@ function httpCandidate(tool, entry2) {
|
|
|
12687
13098
|
}
|
|
12688
13099
|
}
|
|
12689
13100
|
for (const [key, value] of Object.entries(
|
|
12690
|
-
|
|
13101
|
+
object6(entry2["env_http_headers"] ?? {})
|
|
12691
13102
|
)) {
|
|
12692
13103
|
if (typeof value !== "string") {
|
|
12693
13104
|
throw new ProjectError("Environment header references must be strings");
|
|
@@ -12751,7 +13162,7 @@ function candidate(tool, entry2, file2, alias) {
|
|
|
12751
13162
|
}
|
|
12752
13163
|
result["args"] = args;
|
|
12753
13164
|
}
|
|
12754
|
-
const env =
|
|
13165
|
+
const env = object6(entry2["env"] ?? {});
|
|
12755
13166
|
const forwarded = entry2["env_vars"] ?? [];
|
|
12756
13167
|
if (!Array.isArray(forwarded) || !forwarded.every((item) => typeof item === "string")) {
|
|
12757
13168
|
throw new ProjectError(
|
|
@@ -12818,9 +13229,9 @@ function equivalent(alias, left, right) {
|
|
|
12818
13229
|
return JSON.stringify(readMcp("import", { [alias]: left })[alias]) === JSON.stringify(readMcp("import", { [alias]: right })[alias]);
|
|
12819
13230
|
}
|
|
12820
13231
|
function planMcpImport(config, sources2, names = []) {
|
|
12821
|
-
const original =
|
|
12822
|
-
const root =
|
|
12823
|
-
const existing =
|
|
13232
|
+
const original = readFileSync25(config, "utf8");
|
|
13233
|
+
const root = dirname17(resolve14(config));
|
|
13234
|
+
const existing = object6(parse5(original)["mcp"] ?? {});
|
|
12824
13235
|
const additions = {};
|
|
12825
13236
|
const entries = [];
|
|
12826
13237
|
const fingerprints = [];
|
|
@@ -12833,11 +13244,11 @@ function planMcpImport(config, sources2, names = []) {
|
|
|
12833
13244
|
continue;
|
|
12834
13245
|
}
|
|
12835
13246
|
try {
|
|
12836
|
-
const proposed = candidate(read2.tool,
|
|
13247
|
+
const proposed = candidate(read2.tool, object6(raw), read2.path, alias);
|
|
12837
13248
|
readMcp(config, { [alias]: proposed });
|
|
12838
13249
|
const previous = existing[alias] ?? additions[alias];
|
|
12839
13250
|
if (previous !== void 0) {
|
|
12840
|
-
const status = equivalent(alias,
|
|
13251
|
+
const status = equivalent(alias, object6(previous), proposed) ? "unchanged" : "conflict";
|
|
12841
13252
|
entries.push({ alias, source: read2.path, status });
|
|
12842
13253
|
continue;
|
|
12843
13254
|
}
|
|
@@ -12869,11 +13280,11 @@ function writeMcpImport(plan2) {
|
|
|
12869
13280
|
"Resolve blocked or conflicting MCP entries before importing"
|
|
12870
13281
|
);
|
|
12871
13282
|
}
|
|
12872
|
-
if (
|
|
13283
|
+
if (readFileSync25(plan2.config, "utf8") !== plan2.original) {
|
|
12873
13284
|
throw new ProjectError("pmcp.toml changed after planning; plan again");
|
|
12874
13285
|
}
|
|
12875
13286
|
for (const source2 of plan2.sources) {
|
|
12876
|
-
const current =
|
|
13287
|
+
const current = createHash14("sha256").update(readFileSync25(source2.path)).digest("hex");
|
|
12877
13288
|
if (current !== source2.sha256) {
|
|
12878
13289
|
throw new ProjectError("MCP source changed after planning; plan again");
|
|
12879
13290
|
}
|
|
@@ -12883,7 +13294,7 @@ function writeMcpImport(plan2) {
|
|
|
12883
13294
|
|
|
12884
13295
|
${additions}`;
|
|
12885
13296
|
parse5(after);
|
|
12886
|
-
const backup = backupFiles(
|
|
13297
|
+
const backup = backupFiles(dirname17(resolve14(plan2.config)), [
|
|
12887
13298
|
basename6(plan2.config)
|
|
12888
13299
|
]);
|
|
12889
13300
|
writeFileSync6(plan2.config, after);
|
|
@@ -12892,22 +13303,22 @@ ${additions}`;
|
|
|
12892
13303
|
|
|
12893
13304
|
// src/project/mcp-env.ts
|
|
12894
13305
|
import { spawn } from "child_process";
|
|
12895
|
-
import { readFileSync as
|
|
13306
|
+
import { readFileSync as readFileSync26, statSync as statSync12 } from "fs";
|
|
12896
13307
|
import { homedir as homedir5 } from "os";
|
|
12897
|
-
import { resolve as
|
|
13308
|
+
import { resolve as resolve15 } from "path";
|
|
12898
13309
|
import { parse as parse6 } from "smol-toml";
|
|
12899
13310
|
function sourcePath(file2, root) {
|
|
12900
13311
|
if (file2.startsWith("~/")) {
|
|
12901
|
-
return
|
|
13312
|
+
return resolve15(homedir5(), file2.slice(2));
|
|
12902
13313
|
}
|
|
12903
|
-
return
|
|
13314
|
+
return resolve15(root, file2.replaceAll("${PROJECT_ROOT}", root));
|
|
12904
13315
|
}
|
|
12905
13316
|
function sourceValue(file2, pointer) {
|
|
12906
13317
|
try {
|
|
12907
|
-
if (
|
|
13318
|
+
if (statSync12(file2).size > 2 * 1024 * 1024) {
|
|
12908
13319
|
throw new Error("size");
|
|
12909
13320
|
}
|
|
12910
|
-
const text3 =
|
|
13321
|
+
const text3 = readFileSync26(file2, "utf8").replace(/^\uFEFF/u, "");
|
|
12911
13322
|
let value;
|
|
12912
13323
|
if (file2.endsWith(".toml")) {
|
|
12913
13324
|
value = parse6(text3);
|
|
@@ -12988,8 +13399,8 @@ async function execMcp(spec, alias, env) {
|
|
|
12988
13399
|
|
|
12989
13400
|
// src/project/doctor.ts
|
|
12990
13401
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
12991
|
-
import { existsSync as
|
|
12992
|
-
import { join as
|
|
13402
|
+
import { existsSync as existsSync18, readFileSync as readFileSync27 } from "fs";
|
|
13403
|
+
import { join as join23 } from "path";
|
|
12993
13404
|
import { parse as parse7 } from "smol-toml";
|
|
12994
13405
|
var spawnRunner = (command2, args, cwd, env) => {
|
|
12995
13406
|
const result = spawnSync3(command2, [...args], {
|
|
@@ -13066,10 +13477,10 @@ function claude(spec, o) {
|
|
|
13066
13477
|
}
|
|
13067
13478
|
function codex(spec, o) {
|
|
13068
13479
|
const findings = [];
|
|
13069
|
-
const config =
|
|
13480
|
+
const config = join23(o.home, ".codex/config.toml");
|
|
13070
13481
|
let trusted = false;
|
|
13071
|
-
if (
|
|
13072
|
-
const projects = parse7(
|
|
13482
|
+
if (existsSync18(config)) {
|
|
13483
|
+
const projects = parse7(readFileSync27(config, "utf8")).projects ?? {};
|
|
13073
13484
|
const root = spec.root.replace(/\/$/u, "");
|
|
13074
13485
|
trusted = projects[root]?.trust_level === "trusted" || projects[`${root}/`]?.trust_level === "trusted";
|
|
13075
13486
|
}
|
|
@@ -13195,8 +13606,8 @@ function grok(spec, o) {
|
|
|
13195
13606
|
}
|
|
13196
13607
|
function antigravity(spec, options2) {
|
|
13197
13608
|
const foreign = agentNames(spec.root).filter((name) => {
|
|
13198
|
-
const text3 =
|
|
13199
|
-
|
|
13609
|
+
const text3 = readFileSync27(
|
|
13610
|
+
join23(spec.root, ".agents/agents", name, "agent.md"),
|
|
13200
13611
|
"utf8"
|
|
13201
13612
|
);
|
|
13202
13613
|
return splitFrontmatter(text3).meta["tools"] !== void 0;
|
|
@@ -13216,7 +13627,7 @@ function antigravity(spec, options2) {
|
|
|
13216
13627
|
if (serversForTool(spec, "antigravity").length > 0) {
|
|
13217
13628
|
const result = options2.run(
|
|
13218
13629
|
"agy",
|
|
13219
|
-
["plugin", "validate",
|
|
13630
|
+
["plugin", "validate", join23(spec.root, ANTIGRAVITY_MCP_PLUGIN)],
|
|
13220
13631
|
spec.root
|
|
13221
13632
|
);
|
|
13222
13633
|
findings.push(
|
|
@@ -13247,21 +13658,21 @@ function doctor(spec, options2) {
|
|
|
13247
13658
|
}
|
|
13248
13659
|
|
|
13249
13660
|
// src/project/memory.ts
|
|
13250
|
-
import { existsSync as
|
|
13251
|
-
import { isAbsolute as
|
|
13661
|
+
import { existsSync as existsSync19, readFileSync as readFileSync28 } from "fs";
|
|
13662
|
+
import { isAbsolute as isAbsolute7, join as join24, resolve as resolve16 } from "path";
|
|
13252
13663
|
import { parse as parse8 } from "smol-toml";
|
|
13253
|
-
function
|
|
13664
|
+
function object7(value) {
|
|
13254
13665
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
13255
13666
|
throw new Error("Configuration is not an object");
|
|
13256
13667
|
}
|
|
13257
13668
|
return value;
|
|
13258
13669
|
}
|
|
13259
13670
|
function settings(file2, toml = false) {
|
|
13260
|
-
if (!
|
|
13671
|
+
if (!existsSync19(file2)) {
|
|
13261
13672
|
return {};
|
|
13262
13673
|
}
|
|
13263
|
-
const text3 =
|
|
13264
|
-
return
|
|
13674
|
+
const text3 = readFileSync28(file2, "utf8");
|
|
13675
|
+
return object7(toml ? parse8(text3) : JSON.parse(text3));
|
|
13265
13676
|
}
|
|
13266
13677
|
function configuredState(value) {
|
|
13267
13678
|
if (value === true) {
|
|
@@ -13273,11 +13684,11 @@ function configuredState(value) {
|
|
|
13273
13684
|
return "unknown";
|
|
13274
13685
|
}
|
|
13275
13686
|
function located(metadata2, path, pathScope) {
|
|
13276
|
-
return { ...metadata2, path, pathScope, exists:
|
|
13687
|
+
return { ...metadata2, path, pathScope, exists: existsSync19(path) };
|
|
13277
13688
|
}
|
|
13278
13689
|
function metadata(tool, spec, options2) {
|
|
13279
13690
|
const env = options2.env ?? {};
|
|
13280
|
-
const directory = (name, fallback) =>
|
|
13691
|
+
const directory = (name, fallback) => resolve16(spec.root, env[name] ?? join24(options2.home, fallback));
|
|
13281
13692
|
if (tool === "antigravity") {
|
|
13282
13693
|
return {
|
|
13283
13694
|
state: "unsupported",
|
|
@@ -13301,7 +13712,7 @@ function metadata(tool, spec, options2) {
|
|
|
13301
13712
|
state,
|
|
13302
13713
|
source: result.missing ? "Native CLI not installed" : "codex features list; active session overrides are not inspected"
|
|
13303
13714
|
},
|
|
13304
|
-
|
|
13715
|
+
join24(home2, "memories"),
|
|
13305
13716
|
"store"
|
|
13306
13717
|
);
|
|
13307
13718
|
}
|
|
@@ -13314,13 +13725,13 @@ function metadata(tool, spec, options2) {
|
|
|
13314
13725
|
}
|
|
13315
13726
|
if (tool === "claude") {
|
|
13316
13727
|
const home2 = directory("CLAUDE_CONFIG_DIR", ".claude");
|
|
13317
|
-
const user2 = settings(
|
|
13728
|
+
const user2 = settings(join24(home2, "settings.json"));
|
|
13318
13729
|
const state = env["CLAUDE_CODE_DISABLE_AUTO_MEMORY"] === "1" ? "disabled" : configuredState(user2["autoMemoryEnabled"]);
|
|
13319
13730
|
const source2 = "User configuration and disable environment flag; project trust and active session overrides are not inspected";
|
|
13320
13731
|
const override = user2["autoMemoryDirectory"];
|
|
13321
13732
|
if (typeof override === "string") {
|
|
13322
|
-
const path = override.startsWith("~/") ?
|
|
13323
|
-
if (!
|
|
13733
|
+
const path = override.startsWith("~/") ? join24(options2.home, override.slice(2)) : override;
|
|
13734
|
+
if (!isAbsolute7(path)) {
|
|
13324
13735
|
return {
|
|
13325
13736
|
state: "unknown",
|
|
13326
13737
|
source: "Invalid autoMemoryDirectory metadata; value redacted"
|
|
@@ -13332,7 +13743,7 @@ function metadata(tool, spec, options2) {
|
|
|
13332
13743
|
if (env["CLAUDE_CONFIG_DIR"] && projectName && !projectName.includes("/") && !projectName.includes("\\") && projectName !== "." && projectName !== "..") {
|
|
13333
13744
|
return located(
|
|
13334
13745
|
{ state, source: source2 },
|
|
13335
|
-
|
|
13746
|
+
join24(home2, "projects", projectName, "memory"),
|
|
13336
13747
|
"store"
|
|
13337
13748
|
);
|
|
13338
13749
|
}
|
|
@@ -13341,16 +13752,16 @@ function metadata(tool, spec, options2) {
|
|
|
13341
13752
|
state,
|
|
13342
13753
|
source: `${source2}; native project directory encoding is not guessed`
|
|
13343
13754
|
},
|
|
13344
|
-
|
|
13755
|
+
join24(home2, "projects"),
|
|
13345
13756
|
"base"
|
|
13346
13757
|
);
|
|
13347
13758
|
}
|
|
13348
13759
|
if (tool === "grok") {
|
|
13349
|
-
const home2 =
|
|
13350
|
-
const user2 = settings(
|
|
13351
|
-
const project2 = settings(
|
|
13352
|
-
const userMemory = user2["memory"] === void 0 ? {} :
|
|
13353
|
-
const projectMemory = project2["memory"] === void 0 ? {} :
|
|
13760
|
+
const home2 = join24(options2.home, ".grok");
|
|
13761
|
+
const user2 = settings(join24(home2, "config.toml"), true);
|
|
13762
|
+
const project2 = settings(join24(spec.root, ".grok/config.toml"), true);
|
|
13763
|
+
const userMemory = user2["memory"] === void 0 ? {} : object7(user2["memory"]);
|
|
13764
|
+
const projectMemory = project2["memory"] === void 0 ? {} : object7(project2["memory"]);
|
|
13354
13765
|
const overridden = projectMemory["enabled"] !== void 0;
|
|
13355
13766
|
const state = overridden ? "unknown" : configuredState(userMemory["enabled"]);
|
|
13356
13767
|
return located(
|
|
@@ -13358,13 +13769,13 @@ function metadata(tool, spec, options2) {
|
|
|
13358
13769
|
state,
|
|
13359
13770
|
source: "User memory configuration; project trust and workspace encoding are not exposed by a read-only native memory command"
|
|
13360
13771
|
},
|
|
13361
|
-
|
|
13772
|
+
join24(home2, "memory-v2/workspaces"),
|
|
13362
13773
|
"base"
|
|
13363
13774
|
);
|
|
13364
13775
|
}
|
|
13365
13776
|
const home = directory("GEMINI_CLI_HOME", ".gemini");
|
|
13366
|
-
const user = settings(
|
|
13367
|
-
const context = user["context"] === void 0 ? {} :
|
|
13777
|
+
const user = settings(join24(home, "settings.json"));
|
|
13778
|
+
const context = user["context"] === void 0 ? {} : object7(user["context"]);
|
|
13368
13779
|
const names = context["fileName"] ?? "GEMINI.md";
|
|
13369
13780
|
const contextFileNames = typeof names === "string" ? [names] : names;
|
|
13370
13781
|
if (!Array.isArray(contextFileNames) || !contextFileNames.every((name) => typeof name === "string")) {
|
|
@@ -13379,7 +13790,7 @@ function metadata(tool, spec, options2) {
|
|
|
13379
13790
|
source: "Global memory file existence and user context filenames; active memory tool enablement is not exposed",
|
|
13380
13791
|
contextFileNames
|
|
13381
13792
|
},
|
|
13382
|
-
|
|
13793
|
+
join24(home, "GEMINI.md"),
|
|
13383
13794
|
"store"
|
|
13384
13795
|
);
|
|
13385
13796
|
}
|
|
@@ -13412,14 +13823,14 @@ function memoryFindings(spec, options2) {
|
|
|
13412
13823
|
// src/project/edit.ts
|
|
13413
13824
|
import {
|
|
13414
13825
|
cpSync,
|
|
13415
|
-
existsSync as
|
|
13826
|
+
existsSync as existsSync20,
|
|
13416
13827
|
mkdirSync as mkdirSync8,
|
|
13417
|
-
readFileSync as
|
|
13828
|
+
readFileSync as readFileSync29,
|
|
13418
13829
|
rmSync as rmSync5,
|
|
13419
|
-
statSync as
|
|
13830
|
+
statSync as statSync13,
|
|
13420
13831
|
writeFileSync as writeFileSync7
|
|
13421
13832
|
} from "fs";
|
|
13422
|
-
import { basename as basename7, dirname as
|
|
13833
|
+
import { basename as basename7, dirname as dirname18, join as join25 } from "path";
|
|
13423
13834
|
var CROSS_TOOL = ["name", "description"];
|
|
13424
13835
|
var tomlValue2 = (value) => JSON.stringify(value);
|
|
13425
13836
|
function findEntry(entries, wanted) {
|
|
@@ -13443,7 +13854,7 @@ function addEntry(root, entry2) {
|
|
|
13443
13854
|
`${entry2.name} is a ${kind2}; only skills and agents are added to .agents/`
|
|
13444
13855
|
);
|
|
13445
13856
|
}
|
|
13446
|
-
const { meta, body } = splitFrontmatter(
|
|
13857
|
+
const { meta, body } = splitFrontmatter(readFileSync29(entry2.path, "utf8"));
|
|
13447
13858
|
const name = basename7(entry2.slug);
|
|
13448
13859
|
if (!/^[a-z0-9][a-z0-9-]*$/u.test(name)) {
|
|
13449
13860
|
throw new ProjectError(
|
|
@@ -13451,12 +13862,12 @@ function addEntry(root, entry2) {
|
|
|
13451
13862
|
);
|
|
13452
13863
|
}
|
|
13453
13864
|
if (kind2 === "skill") {
|
|
13454
|
-
const dest2 =
|
|
13455
|
-
if (
|
|
13865
|
+
const dest2 = join25(root, ".agents/skills", name);
|
|
13866
|
+
if (existsSync20(dest2))
|
|
13456
13867
|
throw new ProjectError(`.agents/skills/${name} already exists`);
|
|
13457
|
-
const source2 = entry2.root ??
|
|
13458
|
-
mkdirSync8(
|
|
13459
|
-
if (
|
|
13868
|
+
const source2 = entry2.root ?? dirname18(entry2.path);
|
|
13869
|
+
mkdirSync8(dirname18(dest2), { recursive: true });
|
|
13870
|
+
if (statSync13(source2).isDirectory()) {
|
|
13460
13871
|
cpSync(source2, dest2, { recursive: true });
|
|
13461
13872
|
} else {
|
|
13462
13873
|
mkdirSync8(dest2);
|
|
@@ -13467,14 +13878,14 @@ function addEntry(root, entry2) {
|
|
|
13467
13878
|
description: meta["description"] ?? entry2.description
|
|
13468
13879
|
});
|
|
13469
13880
|
writeFileSync7(
|
|
13470
|
-
|
|
13881
|
+
join25(dest2, "SKILL.md"),
|
|
13471
13882
|
`${head}
|
|
13472
13883
|
${body.replace(/^\n/u, "")}`
|
|
13473
13884
|
);
|
|
13474
13885
|
return { kind: kind2, name, path: `.agents/skills/${name}` };
|
|
13475
13886
|
}
|
|
13476
|
-
const dest =
|
|
13477
|
-
if (
|
|
13887
|
+
const dest = join25(root, ".agents/agents", name);
|
|
13888
|
+
if (existsSync20(dest))
|
|
13478
13889
|
throw new ProjectError(`.agents/agents/${name} already exists`);
|
|
13479
13890
|
const kept = {};
|
|
13480
13891
|
const moved = {};
|
|
@@ -13484,19 +13895,19 @@ ${body.replace(/^\n/u, "")}`
|
|
|
13484
13895
|
}
|
|
13485
13896
|
mkdirSync8(dest, { recursive: true });
|
|
13486
13897
|
writeFileSync7(
|
|
13487
|
-
|
|
13898
|
+
join25(dest, "agent.md"),
|
|
13488
13899
|
`${renderFrontmatter(kept)}
|
|
13489
13900
|
${body.replace(/^\n/u, "")}`
|
|
13490
13901
|
);
|
|
13491
13902
|
if (Object.keys(moved).length > 0) {
|
|
13492
|
-
const config =
|
|
13903
|
+
const config = join25(root, "pmcp.toml");
|
|
13493
13904
|
const lines2 = [
|
|
13494
13905
|
`[agents.${name}.claude]`,
|
|
13495
13906
|
...Object.entries(moved).map(([k, v]) => `${k} = ${tomlValue2(v)}`)
|
|
13496
13907
|
];
|
|
13497
|
-
const before =
|
|
13498
|
-
const
|
|
13499
|
-
writeFileSync7(config, `${before}${
|
|
13908
|
+
const before = existsSync20(config) ? readFileSync29(config, "utf8") : "";
|
|
13909
|
+
const sep7 = before === "" || before.endsWith("\n\n") ? "" : "\n";
|
|
13910
|
+
writeFileSync7(config, `${before}${sep7}${lines2.join("\n")}
|
|
13500
13911
|
`);
|
|
13501
13912
|
}
|
|
13502
13913
|
return { kind: kind2, name, path: `.agents/agents/${name}/agent.md` };
|
|
@@ -13517,15 +13928,15 @@ function withoutTables(toml, name) {
|
|
|
13517
13928
|
function removeAsset(root, name) {
|
|
13518
13929
|
const removed = [];
|
|
13519
13930
|
for (const rel of [`.agents/skills/${name}`, `.agents/agents/${name}`]) {
|
|
13520
|
-
if (!
|
|
13521
|
-
rmSync5(
|
|
13931
|
+
if (!existsSync20(join25(root, rel))) continue;
|
|
13932
|
+
rmSync5(join25(root, rel), { recursive: true, force: true });
|
|
13522
13933
|
removed.push(rel);
|
|
13523
13934
|
}
|
|
13524
13935
|
if (removed.length === 0)
|
|
13525
13936
|
throw new ProjectError(`no skill or agent named ${name} in .agents/`);
|
|
13526
|
-
const config =
|
|
13527
|
-
if (
|
|
13528
|
-
const before =
|
|
13937
|
+
const config = join25(root, "pmcp.toml");
|
|
13938
|
+
if (existsSync20(config)) {
|
|
13939
|
+
const before = readFileSync29(config, "utf8");
|
|
13529
13940
|
const after = withoutTables(before, name);
|
|
13530
13941
|
if (after !== before) writeFileSync7(config, after);
|
|
13531
13942
|
}
|
|
@@ -13764,16 +14175,16 @@ var doctorCommand = {
|
|
|
13764
14175
|
};
|
|
13765
14176
|
|
|
13766
14177
|
// src/validate.ts
|
|
13767
|
-
import { existsSync as
|
|
13768
|
-
import { basename as basename8, join as
|
|
14178
|
+
import { existsSync as existsSync21, readFileSync as readFileSync30, statSync as statSync14 } from "fs";
|
|
14179
|
+
import { basename as basename8, join as join26, resolve as resolve17 } from "path";
|
|
13769
14180
|
function validateSkillDir(dir) {
|
|
13770
|
-
const root =
|
|
13771
|
-
const skillMd = basename8(root) === "SKILL.md" ? root :
|
|
13772
|
-
if (!
|
|
14181
|
+
const root = resolve17(dir);
|
|
14182
|
+
const skillMd = basename8(root) === "SKILL.md" ? root : join26(root, "SKILL.md");
|
|
14183
|
+
if (!existsSync21(skillMd) || !statSync14(skillMd).isFile()) {
|
|
13773
14184
|
return [{ path: root, reason: "no SKILL.md", level: "error" }];
|
|
13774
14185
|
}
|
|
13775
|
-
const front = frontmatterObject(
|
|
13776
|
-
const directory = basename8(skillMd === root ?
|
|
14186
|
+
const front = frontmatterObject(readFileSync30(skillMd, "utf8"));
|
|
14187
|
+
const directory = basename8(skillMd === root ? join26(root, "..") : root);
|
|
13777
14188
|
return specProblems(front, directory).map((reason) => ({
|
|
13778
14189
|
path: skillMd,
|
|
13779
14190
|
reason,
|
|
@@ -13818,8 +14229,8 @@ var validateCommand = {
|
|
|
13818
14229
|
};
|
|
13819
14230
|
|
|
13820
14231
|
// src/commands/mailbox.ts
|
|
13821
|
-
import { readFileSync as
|
|
13822
|
-
import { resolve as
|
|
14232
|
+
import { readFileSync as readFileSync31, writeFileSync as writeFileSync8 } from "fs";
|
|
14233
|
+
import { resolve as resolve18 } from "path";
|
|
13823
14234
|
init_config2();
|
|
13824
14235
|
init_service();
|
|
13825
14236
|
init_schemas();
|
|
@@ -13902,7 +14313,7 @@ var mailboxCommand = {
|
|
|
13902
14313
|
}
|
|
13903
14314
|
if (verb === "init") {
|
|
13904
14315
|
const config2 = initializeMailboxConfig(
|
|
13905
|
-
|
|
14316
|
+
resolve18(context.cwd, one(context.args, "config") ?? "pmcp.toml"),
|
|
13906
14317
|
one(context.args, "state-dir")
|
|
13907
14318
|
);
|
|
13908
14319
|
const store2 = await MailboxStore.open(config2);
|
|
@@ -13927,7 +14338,7 @@ var mailboxCommand = {
|
|
|
13927
14338
|
if (verb === "restore") {
|
|
13928
14339
|
await restoreBackup(required(context, "input"), {
|
|
13929
14340
|
...config,
|
|
13930
|
-
databasePath:
|
|
14341
|
+
databasePath: resolve18(context.cwd, required(context, "output"))
|
|
13931
14342
|
});
|
|
13932
14343
|
context.ui.data(
|
|
13933
14344
|
`${JSON.stringify({ ok: true, data: { restored: true } })}
|
|
@@ -14001,7 +14412,7 @@ var mailboxCommand = {
|
|
|
14001
14412
|
ownsBinding = true;
|
|
14002
14413
|
}
|
|
14003
14414
|
const inputFile = one(context.args, "input");
|
|
14004
|
-
const input = inputFile ? JSON.parse(
|
|
14415
|
+
const input = inputFile ? JSON.parse(readFileSync31(inputFile, "utf8")) : {};
|
|
14005
14416
|
const result = await new MailboxService(store, binding).call(
|
|
14006
14417
|
operation,
|
|
14007
14418
|
input
|
|
@@ -14085,7 +14496,7 @@ var harnessOptions = [
|
|
|
14085
14496
|
];
|
|
14086
14497
|
|
|
14087
14498
|
// src/commands/harness/invocation.ts
|
|
14088
|
-
import { readFileSync as
|
|
14499
|
+
import { readFileSync as readFileSync32, statSync as statSync15 } from "fs";
|
|
14089
14500
|
init_validation();
|
|
14090
14501
|
var common = ["group-file", "generation"];
|
|
14091
14502
|
var options = {
|
|
@@ -14122,10 +14533,10 @@ var options = {
|
|
|
14122
14533
|
"recovery:abort-start": [...common, "session"]
|
|
14123
14534
|
};
|
|
14124
14535
|
function file(path) {
|
|
14125
|
-
const stat =
|
|
14536
|
+
const stat = statSync15(path);
|
|
14126
14537
|
if (!stat.isFile() || stat.size > 65536)
|
|
14127
14538
|
throw new ArgumentError("Input file exceeds 65536 bytes");
|
|
14128
|
-
return
|
|
14539
|
+
return readFileSync32(path, "utf8");
|
|
14129
14540
|
}
|
|
14130
14541
|
function parseInvocation(context) {
|
|
14131
14542
|
const [family, action, ...extra] = context.args.positional;
|
|
@@ -14161,7 +14572,7 @@ function parseInvocation(context) {
|
|
|
14161
14572
|
action,
|
|
14162
14573
|
groupFile: one(context.args, "group-file"),
|
|
14163
14574
|
generation: one(context.args, "generation"),
|
|
14164
|
-
input: inputFile ?
|
|
14575
|
+
input: inputFile ? object8(JSON.parse(file(inputFile))) : void 0,
|
|
14165
14576
|
sessionId: one(context.args, "session"),
|
|
14166
14577
|
leaseFile: one(context.args, "lease-file"),
|
|
14167
14578
|
controller: one(context.args, "controller"),
|
|
@@ -14364,6 +14775,198 @@ var tuiCommand = {
|
|
|
14364
14775
|
}
|
|
14365
14776
|
};
|
|
14366
14777
|
|
|
14778
|
+
// src/commands/marketplace.ts
|
|
14779
|
+
import { resolve as resolve21 } from "path";
|
|
14780
|
+
|
|
14781
|
+
// src/marketplace/export.ts
|
|
14782
|
+
import {
|
|
14783
|
+
existsSync as existsSync33,
|
|
14784
|
+
lstatSync as lstatSync12,
|
|
14785
|
+
mkdirSync as mkdirSync12,
|
|
14786
|
+
readFileSync as readFileSync37,
|
|
14787
|
+
readdirSync as readdirSync13,
|
|
14788
|
+
writeFileSync as writeFileSync14
|
|
14789
|
+
} from "fs";
|
|
14790
|
+
import { dirname as dirname24, join as join52, resolve as resolve20 } from "path";
|
|
14791
|
+
var NATIVE_PLUGIN_RUNTIMES = [
|
|
14792
|
+
"claude",
|
|
14793
|
+
"codex",
|
|
14794
|
+
"gemini",
|
|
14795
|
+
"grok",
|
|
14796
|
+
"agy"
|
|
14797
|
+
];
|
|
14798
|
+
function isNativePluginRuntime(value) {
|
|
14799
|
+
return NATIVE_PLUGIN_RUNTIMES.includes(value);
|
|
14800
|
+
}
|
|
14801
|
+
function publicPlugins(root) {
|
|
14802
|
+
const rejected = [];
|
|
14803
|
+
const marketplace = readLocalMarketplace(
|
|
14804
|
+
root,
|
|
14805
|
+
(item) => rejected.push(item.reason)
|
|
14806
|
+
);
|
|
14807
|
+
if (!marketplace || rejected.length)
|
|
14808
|
+
throw new Error(`Invalid public marketplace: ${rejected.join("; ")}`);
|
|
14809
|
+
return marketplace.plugins;
|
|
14810
|
+
}
|
|
14811
|
+
function readResources(directory, prefix, files) {
|
|
14812
|
+
for (const entry2 of readdirSync13(directory, { withFileTypes: true })) {
|
|
14813
|
+
if (entry2.isSymbolicLink())
|
|
14814
|
+
throw new Error("Plugin exports cannot contain symlinks");
|
|
14815
|
+
const path = join52(directory, entry2.name);
|
|
14816
|
+
const relativePath = `${prefix}/${entry2.name}`;
|
|
14817
|
+
if (entry2.isDirectory()) {
|
|
14818
|
+
if (relativePath.split("/").length > 6)
|
|
14819
|
+
throw new Error("Plugin resources exceed the supported depth");
|
|
14820
|
+
readResources(path, relativePath, files);
|
|
14821
|
+
continue;
|
|
14822
|
+
}
|
|
14823
|
+
if (!entry2.isFile())
|
|
14824
|
+
throw new Error("Plugin resources must be regular files");
|
|
14825
|
+
const stats = lstatSync12(path);
|
|
14826
|
+
if (stats.size > 1024 * 1024 || files.size >= 256)
|
|
14827
|
+
throw new Error("Plugin resources exceed the export budget");
|
|
14828
|
+
files.set(relativePath, readFileSync37(path));
|
|
14829
|
+
}
|
|
14830
|
+
}
|
|
14831
|
+
function pluginExportFiles(root, name, runtime2) {
|
|
14832
|
+
const plugin = publicPlugins(root).find((item) => item.name === name);
|
|
14833
|
+
if (!plugin) throw new Error("No such public plugin");
|
|
14834
|
+
const files = /* @__PURE__ */ new Map();
|
|
14835
|
+
readResources(containedPath(plugin.directory, "skills"), "skills", files);
|
|
14836
|
+
for (const name2 of ["LICENSE", "NOTICE"])
|
|
14837
|
+
files.set(name2, readFileSync37(containedPath(root, name2)));
|
|
14838
|
+
const manifests = {
|
|
14839
|
+
claude: [".claude-plugin/plugin.json", ".mcp.json"],
|
|
14840
|
+
codex: ["plugin.json", "mcp.json"],
|
|
14841
|
+
gemini: ["gemini-extension.json"],
|
|
14842
|
+
grok: [".claude-plugin/plugin.json", ".mcp.json"],
|
|
14843
|
+
agy: []
|
|
14844
|
+
};
|
|
14845
|
+
for (const filename of manifests[runtime2]) {
|
|
14846
|
+
if (existsSync33(join52(plugin.directory, filename)))
|
|
14847
|
+
files.set(
|
|
14848
|
+
filename,
|
|
14849
|
+
readFileSync37(containedPath(plugin.directory, filename))
|
|
14850
|
+
);
|
|
14851
|
+
}
|
|
14852
|
+
if (runtime2 === "agy") {
|
|
14853
|
+
files.set(
|
|
14854
|
+
"plugin.json",
|
|
14855
|
+
Buffer.from(
|
|
14856
|
+
`${JSON.stringify({ name: plugin.name, description: plugin.manifest.description }, null, 2)}
|
|
14857
|
+
`
|
|
14858
|
+
)
|
|
14859
|
+
);
|
|
14860
|
+
const mcp = join52(plugin.directory, ".mcp.json");
|
|
14861
|
+
if (existsSync33(mcp))
|
|
14862
|
+
files.set(
|
|
14863
|
+
"mcp_config.json",
|
|
14864
|
+
readFileSync37(containedPath(plugin.directory, mcp))
|
|
14865
|
+
);
|
|
14866
|
+
}
|
|
14867
|
+
return files;
|
|
14868
|
+
}
|
|
14869
|
+
function exportPublicPlugin(root, name, runtime2, output) {
|
|
14870
|
+
const destination = resolve20(output);
|
|
14871
|
+
const files = pluginExportFiles(root, name, runtime2);
|
|
14872
|
+
mkdirSync12(dirname24(destination), { recursive: true });
|
|
14873
|
+
try {
|
|
14874
|
+
mkdirSync12(destination);
|
|
14875
|
+
} catch (error) {
|
|
14876
|
+
if (error.code === "EEXIST")
|
|
14877
|
+
throw new Error(
|
|
14878
|
+
"Export destination already exists; choose a new directory"
|
|
14879
|
+
);
|
|
14880
|
+
throw error;
|
|
14881
|
+
}
|
|
14882
|
+
for (const [path, bytes] of files) {
|
|
14883
|
+
const target2 = join52(destination, path);
|
|
14884
|
+
mkdirSync12(dirname24(target2), { recursive: true });
|
|
14885
|
+
writeFileSync14(target2, bytes, { flag: "wx" });
|
|
14886
|
+
}
|
|
14887
|
+
return { plugin: name, runtime: runtime2, output: destination, files: files.size };
|
|
14888
|
+
}
|
|
14889
|
+
|
|
14890
|
+
// src/commands/marketplace.ts
|
|
14891
|
+
var marketplaceCommand = {
|
|
14892
|
+
name: "marketplace",
|
|
14893
|
+
describe: "List free public plugins or export a selected native plugin",
|
|
14894
|
+
usage: "pmcp marketplace list | export <plugin> --runtime <runtime> --output <directory> [--json]",
|
|
14895
|
+
options: [
|
|
14896
|
+
{
|
|
14897
|
+
name: "runtime",
|
|
14898
|
+
describe: "Native plugin format: claude, codex, gemini, grok or agy.",
|
|
14899
|
+
placeholder: "<runtime>"
|
|
14900
|
+
},
|
|
14901
|
+
{
|
|
14902
|
+
name: "output",
|
|
14903
|
+
describe: "New directory for the selected plugin; existing paths are preserved.",
|
|
14904
|
+
placeholder: "<directory>"
|
|
14905
|
+
},
|
|
14906
|
+
{
|
|
14907
|
+
name: "json",
|
|
14908
|
+
describe: "Print machine-readable results.",
|
|
14909
|
+
boolean: true
|
|
14910
|
+
}
|
|
14911
|
+
],
|
|
14912
|
+
run(context) {
|
|
14913
|
+
const [verb, name, ...extra] = context.args.positional;
|
|
14914
|
+
const root = publicMarketplaceRoot();
|
|
14915
|
+
if (verb === "list") {
|
|
14916
|
+
if (name || extra.length || one(context.args, "runtime") || one(context.args, "output"))
|
|
14917
|
+
throw new ArgumentError(
|
|
14918
|
+
"marketplace list takes no plugin or export options"
|
|
14919
|
+
);
|
|
14920
|
+
const plugins = publicPlugins(root).map((plugin) => ({
|
|
14921
|
+
name: plugin.name,
|
|
14922
|
+
version: plugin.manifest.version,
|
|
14923
|
+
description: plugin.manifest.description
|
|
14924
|
+
}));
|
|
14925
|
+
if (context.args.flags.has("json")) {
|
|
14926
|
+
context.ui.data(
|
|
14927
|
+
`${JSON.stringify({ marketplace: "pmcp", plugins }, null, 2)}
|
|
14928
|
+
`
|
|
14929
|
+
);
|
|
14930
|
+
return 0;
|
|
14931
|
+
}
|
|
14932
|
+
context.ui.table(
|
|
14933
|
+
plugins.map((plugin) => [
|
|
14934
|
+
plugin.name,
|
|
14935
|
+
String(plugin.description ?? "")
|
|
14936
|
+
])
|
|
14937
|
+
);
|
|
14938
|
+
return 0;
|
|
14939
|
+
}
|
|
14940
|
+
if (verb !== "export" || !name || extra.length)
|
|
14941
|
+
throw new ArgumentError(
|
|
14942
|
+
"Use marketplace list or export with exactly one plugin"
|
|
14943
|
+
);
|
|
14944
|
+
const output = one(context.args, "output");
|
|
14945
|
+
if (!output)
|
|
14946
|
+
throw new ArgumentError("Provide --output for the exported plugin");
|
|
14947
|
+
const runtime2 = one(context.args, "runtime");
|
|
14948
|
+
if (!isNativePluginRuntime(runtime2))
|
|
14949
|
+
throw new ArgumentError(
|
|
14950
|
+
"Choose --runtime claude, codex, gemini, grok or agy"
|
|
14951
|
+
);
|
|
14952
|
+
const result = exportPublicPlugin(
|
|
14953
|
+
root,
|
|
14954
|
+
name,
|
|
14955
|
+
runtime2,
|
|
14956
|
+
resolve21(context.cwd, output)
|
|
14957
|
+
);
|
|
14958
|
+
if (context.args.flags.has("json")) {
|
|
14959
|
+
context.ui.data(`${JSON.stringify(result, null, 2)}
|
|
14960
|
+
`);
|
|
14961
|
+
return 0;
|
|
14962
|
+
}
|
|
14963
|
+
context.ui.success(
|
|
14964
|
+
`Exported ${name} for ${runtime2} to ${result.output}; install with the native plugin manager`
|
|
14965
|
+
);
|
|
14966
|
+
return 0;
|
|
14967
|
+
}
|
|
14968
|
+
};
|
|
14969
|
+
|
|
14367
14970
|
// src/commands/index.ts
|
|
14368
14971
|
var COMMANDS = [
|
|
14369
14972
|
serveCommand,
|
|
@@ -14382,7 +14985,8 @@ var COMMANDS = [
|
|
|
14382
14985
|
validateCommand,
|
|
14383
14986
|
mailboxCommand,
|
|
14384
14987
|
harnessCommand,
|
|
14385
|
-
tuiCommand
|
|
14988
|
+
tuiCommand,
|
|
14989
|
+
marketplaceCommand
|
|
14386
14990
|
];
|
|
14387
14991
|
var PLANNED = [];
|
|
14388
14992
|
var GLOBAL = /* @__PURE__ */ new Set(["-q", "--quiet", "--no-color", "--color"]);
|
|
@@ -14428,7 +15032,7 @@ function dispatch(argv, options2 = {}) {
|
|
|
14428
15032
|
}
|
|
14429
15033
|
return run(command2, rest, { ui, env, cwd });
|
|
14430
15034
|
}
|
|
14431
|
-
var VERSION = "0.
|
|
15035
|
+
var VERSION = "0.14.1";
|
|
14432
15036
|
async function run(command2, argv, context) {
|
|
14433
15037
|
try {
|
|
14434
15038
|
return await command2.run({
|