taskchef 8.6.2 → 8.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +8 -4
- package/assets/README.md +19 -0
- package/assets/taskchef-app-rounded.png +0 -0
- package/assets/taskchef-avatar-square.png +0 -0
- package/assets/taskchef-avatar-square.svg +8 -0
- package/assets/taskchef-sidebar-dark.svg +6 -0
- package/assets/taskchef-sidebar-light.svg +6 -0
- package/assets/taskchef-sidebar.svg +6 -0
- package/docs/advanced-guide.md +20 -29
- package/docs/dashboard-ui.md +15 -0
- package/docs/diagrams/taskchef-next-reply-lookup.drawio +38 -0
- package/docs/diagrams/taskchef-next-reply-lookup.drawio.png +0 -0
- package/docs/diagrams/taskchef-next-storage.drawio +41 -0
- package/docs/diagrams/taskchef-next-storage.drawio.png +0 -0
- package/docs/images/taskchef-pr-icon-comparison.jpg +0 -0
- package/docs/images/taskchef-unified-status-popup.jpg +0 -0
- package/docs/mcp-app.md +261 -6
- package/docs/spec.md +24 -69
- package/docs/taskchef-next-github.md +206 -0
- package/docs/taskchef-next-status-research.md +102 -0
- package/docs/taskchef-next-storage.md +167 -0
- package/docs/workflows.md +4 -2
- package/node_modules/@napi-rs/keyring/LICENSE +21 -0
- package/node_modules/@napi-rs/keyring/README.md +45 -0
- package/node_modules/@napi-rs/keyring/index.d.ts +232 -0
- package/node_modules/@napi-rs/keyring/index.js +706 -0
- package/node_modules/@napi-rs/keyring/keytar.d.ts +53 -0
- package/node_modules/@napi-rs/keyring/keytar.js +26 -0
- package/node_modules/@napi-rs/keyring/package.json +126 -0
- package/node_modules/@napi-rs/keyring-darwin-arm64/README.md +3 -0
- package/node_modules/@napi-rs/keyring-darwin-arm64/keyring.darwin-arm64.node +0 -0
- package/node_modules/@napi-rs/keyring-darwin-arm64/package.json +32 -0
- package/node_modules/@napi-rs/keyring-darwin-x64/README.md +3 -0
- package/node_modules/@napi-rs/keyring-darwin-x64/keyring.darwin-x64.node +0 -0
- package/node_modules/@napi-rs/keyring-darwin-x64/package.json +32 -0
- package/node_modules/@napi-rs/keyring-linux-arm64-gnu/README.md +3 -0
- package/node_modules/@napi-rs/keyring-linux-arm64-gnu/keyring.linux-arm64-gnu.node +0 -0
- package/node_modules/@napi-rs/keyring-linux-arm64-gnu/package.json +35 -0
- package/node_modules/@napi-rs/keyring-linux-arm64-musl/README.md +3 -0
- package/node_modules/@napi-rs/keyring-linux-arm64-musl/keyring.linux-arm64-musl.node +0 -0
- package/node_modules/@napi-rs/keyring-linux-arm64-musl/package.json +35 -0
- package/node_modules/@napi-rs/keyring-linux-x64-gnu/README.md +3 -0
- package/node_modules/@napi-rs/keyring-linux-x64-gnu/keyring.linux-x64-gnu.node +0 -0
- package/node_modules/@napi-rs/keyring-linux-x64-gnu/package.json +35 -0
- package/node_modules/@napi-rs/keyring-linux-x64-musl/README.md +3 -0
- package/node_modules/@napi-rs/keyring-linux-x64-musl/keyring.linux-x64-musl.node +0 -0
- package/node_modules/@napi-rs/keyring-linux-x64-musl/package.json +35 -0
- package/node_modules/@napi-rs/keyring-win32-arm64-msvc/README.md +3 -0
- package/node_modules/@napi-rs/keyring-win32-arm64-msvc/keyring.win32-arm64-msvc.node +0 -0
- package/node_modules/@napi-rs/keyring-win32-arm64-msvc/package.json +32 -0
- package/node_modules/@napi-rs/keyring-win32-x64-msvc/README.md +3 -0
- package/node_modules/@napi-rs/keyring-win32-x64-msvc/keyring.win32-x64-msvc.node +0 -0
- package/node_modules/@napi-rs/keyring-win32-x64-msvc/package.json +32 -0
- package/node_modules/@types/debug/LICENSE +21 -0
- package/node_modules/@types/debug/README.md +69 -0
- package/node_modules/@types/debug/index.d.ts +50 -0
- package/node_modules/@types/debug/package.json +58 -0
- package/node_modules/@types/mdast/LICENSE +21 -0
- package/node_modules/@types/mdast/README.md +15 -0
- package/node_modules/@types/mdast/index.d.ts +1123 -0
- package/node_modules/@types/mdast/package.json +47 -0
- package/node_modules/@types/ms/LICENSE +21 -0
- package/node_modules/@types/ms/README.md +82 -0
- package/node_modules/@types/ms/index.d.ts +63 -0
- package/node_modules/@types/ms/package.json +26 -0
- package/node_modules/@types/unist/LICENSE +21 -0
- package/node_modules/@types/unist/README.md +15 -0
- package/node_modules/@types/unist/index.d.ts +119 -0
- package/node_modules/@types/unist/package.json +60 -0
- package/node_modules/character-entities/index.d.ts +6 -0
- package/node_modules/character-entities/index.js +2132 -0
- package/node_modules/character-entities/license +22 -0
- package/node_modules/character-entities/package.json +78 -0
- package/node_modules/character-entities/readme.md +152 -0
- package/node_modules/decode-named-character-reference/index.d.ts +13 -0
- package/node_modules/decode-named-character-reference/index.d.ts.map +1 -0
- package/node_modules/decode-named-character-reference/index.dom.d.ts +6 -0
- package/node_modules/decode-named-character-reference/index.dom.d.ts.map +1 -0
- package/node_modules/decode-named-character-reference/index.dom.js +32 -0
- package/node_modules/decode-named-character-reference/index.js +19 -0
- package/node_modules/decode-named-character-reference/license +22 -0
- package/node_modules/decode-named-character-reference/package.json +90 -0
- package/node_modules/decode-named-character-reference/readme.md +136 -0
- package/node_modules/dequal/dist/index.js +86 -0
- package/node_modules/dequal/dist/index.min.js +1 -0
- package/node_modules/dequal/dist/index.mjs +84 -0
- package/node_modules/dequal/index.d.ts +1 -0
- package/node_modules/dequal/license +21 -0
- package/node_modules/dequal/lite/index.d.ts +1 -0
- package/node_modules/dequal/lite/index.js +31 -0
- package/node_modules/dequal/lite/index.min.js +1 -0
- package/node_modules/dequal/lite/index.mjs +29 -0
- package/node_modules/dequal/package.json +57 -0
- package/node_modules/dequal/readme.md +112 -0
- package/node_modules/devlop/lib/default.js +9 -0
- package/node_modules/devlop/lib/development.d.ts +84 -0
- package/node_modules/devlop/lib/development.js +238 -0
- package/node_modules/devlop/license +22 -0
- package/node_modules/devlop/package.json +80 -0
- package/node_modules/devlop/readme.md +360 -0
- package/node_modules/mdast-util-from-markdown/dev/index.d.ts +23 -0
- package/node_modules/mdast-util-from-markdown/dev/index.js +2 -0
- package/node_modules/mdast-util-from-markdown/dev/lib/index.d.ts +53 -0
- package/node_modules/mdast-util-from-markdown/dev/lib/index.d.ts.map +1 -0
- package/node_modules/mdast-util-from-markdown/dev/lib/index.js +1348 -0
- package/node_modules/mdast-util-from-markdown/dev/lib/types.d.ts +295 -0
- package/node_modules/mdast-util-from-markdown/dev/lib/types.js +2 -0
- package/node_modules/mdast-util-from-markdown/index.d.ts +23 -0
- package/node_modules/mdast-util-from-markdown/index.js +2 -0
- package/node_modules/mdast-util-from-markdown/lib/index.d.ts +53 -0
- package/node_modules/mdast-util-from-markdown/lib/index.js +1177 -0
- package/node_modules/mdast-util-from-markdown/lib/types.d.ts +295 -0
- package/node_modules/mdast-util-from-markdown/lib/types.js +2 -0
- package/node_modules/mdast-util-from-markdown/license +22 -0
- package/node_modules/mdast-util-from-markdown/package.json +139 -0
- package/node_modules/mdast-util-from-markdown/readme.md +537 -0
- package/node_modules/mdast-util-to-string/index.d.ts +2 -0
- package/node_modules/mdast-util-to-string/index.js +5 -0
- package/node_modules/mdast-util-to-string/lib/index.d.ts +31 -0
- package/node_modules/mdast-util-to-string/lib/index.js +108 -0
- package/node_modules/mdast-util-to-string/license +22 -0
- package/node_modules/mdast-util-to-string/package.json +78 -0
- package/node_modules/mdast-util-to-string/readme.md +218 -0
- package/node_modules/micromark/dev/index.d.ts +82 -0
- package/node_modules/micromark/dev/index.d.ts.map +1 -0
- package/node_modules/micromark/dev/index.js +68 -0
- package/node_modules/micromark/dev/lib/compile.d.ts +16 -0
- package/node_modules/micromark/dev/lib/compile.d.ts.map +1 -0
- package/node_modules/micromark/dev/lib/compile.js +1162 -0
- package/node_modules/micromark/dev/lib/constructs.d.ts +73 -0
- package/node_modules/micromark/dev/lib/constructs.d.ts.map +1 -0
- package/node_modules/micromark/dev/lib/constructs.js +101 -0
- package/node_modules/micromark/dev/lib/create-tokenizer.d.ts +46 -0
- package/node_modules/micromark/dev/lib/create-tokenizer.d.ts.map +1 -0
- package/node_modules/micromark/dev/lib/create-tokenizer.js +718 -0
- package/node_modules/micromark/dev/lib/initialize/content.d.ts +4 -0
- package/node_modules/micromark/dev/lib/initialize/content.d.ts.map +1 -0
- package/node_modules/micromark/dev/lib/initialize/content.js +99 -0
- package/node_modules/micromark/dev/lib/initialize/document.d.ts +10 -0
- package/node_modules/micromark/dev/lib/initialize/document.d.ts.map +1 -0
- package/node_modules/micromark/dev/lib/initialize/document.js +449 -0
- package/node_modules/micromark/dev/lib/initialize/flow.d.ts +4 -0
- package/node_modules/micromark/dev/lib/initialize/flow.d.ts.map +1 -0
- package/node_modules/micromark/dev/lib/initialize/flow.js +86 -0
- package/node_modules/micromark/dev/lib/initialize/text.d.ts +8 -0
- package/node_modules/micromark/dev/lib/initialize/text.d.ts.map +1 -0
- package/node_modules/micromark/dev/lib/initialize/text.js +248 -0
- package/node_modules/micromark/dev/lib/parse.d.ts +10 -0
- package/node_modules/micromark/dev/lib/parse.d.ts.map +1 -0
- package/node_modules/micromark/dev/lib/parse.js +58 -0
- package/node_modules/micromark/dev/lib/postprocess.d.ts +9 -0
- package/node_modules/micromark/dev/lib/postprocess.d.ts.map +1 -0
- package/node_modules/micromark/dev/lib/postprocess.js +19 -0
- package/node_modules/micromark/dev/lib/preprocess.d.ts +13 -0
- package/node_modules/micromark/dev/lib/preprocess.d.ts.map +1 -0
- package/node_modules/micromark/dev/lib/preprocess.js +139 -0
- package/node_modules/micromark/dev/stream.d.ts +35 -0
- package/node_modules/micromark/dev/stream.d.ts.map +1 -0
- package/node_modules/micromark/dev/stream.js +270 -0
- package/node_modules/micromark/index.d.ts +82 -0
- package/node_modules/micromark/index.d.ts.map +1 -0
- package/node_modules/micromark/index.js +60 -0
- package/node_modules/micromark/lib/compile.d.ts +16 -0
- package/node_modules/micromark/lib/compile.d.ts.map +1 -0
- package/node_modules/micromark/lib/compile.js +1068 -0
- package/node_modules/micromark/lib/constructs.d.ts +73 -0
- package/node_modules/micromark/lib/constructs.d.ts.map +1 -0
- package/node_modules/micromark/lib/constructs.js +85 -0
- package/node_modules/micromark/lib/create-tokenizer.d.ts +46 -0
- package/node_modules/micromark/lib/create-tokenizer.d.ts.map +1 -0
- package/node_modules/micromark/lib/create-tokenizer.js +613 -0
- package/node_modules/micromark/lib/initialize/content.d.ts +4 -0
- package/node_modules/micromark/lib/initialize/content.d.ts.map +1 -0
- package/node_modules/micromark/lib/initialize/content.js +79 -0
- package/node_modules/micromark/lib/initialize/document.d.ts +10 -0
- package/node_modules/micromark/lib/initialize/document.d.ts.map +1 -0
- package/node_modules/micromark/lib/initialize/document.js +370 -0
- package/node_modules/micromark/lib/initialize/flow.d.ts +4 -0
- package/node_modules/micromark/lib/initialize/flow.d.ts.map +1 -0
- package/node_modules/micromark/lib/initialize/flow.js +58 -0
- package/node_modules/micromark/lib/initialize/text.d.ts +8 -0
- package/node_modules/micromark/lib/initialize/text.d.ts.map +1 -0
- package/node_modules/micromark/lib/initialize/text.js +216 -0
- package/node_modules/micromark/lib/parse.d.ts +10 -0
- package/node_modules/micromark/lib/parse.d.ts.map +1 -0
- package/node_modules/micromark/lib/parse.js +56 -0
- package/node_modules/micromark/lib/postprocess.d.ts +9 -0
- package/node_modules/micromark/lib/postprocess.d.ts.map +1 -0
- package/node_modules/micromark/lib/postprocess.js +18 -0
- package/node_modules/micromark/lib/preprocess.d.ts +13 -0
- package/node_modules/micromark/lib/preprocess.d.ts.map +1 -0
- package/node_modules/micromark/lib/preprocess.js +112 -0
- package/node_modules/micromark/license +22 -0
- package/node_modules/micromark/package.json +87 -0
- package/node_modules/micromark/readme.md +488 -0
- package/node_modules/micromark/stream.d.ts +35 -0
- package/node_modules/micromark/stream.d.ts.map +1 -0
- package/node_modules/micromark/stream.js +256 -0
- package/node_modules/micromark-core-commonmark/dev/index.d.ts +23 -0
- package/node_modules/micromark-core-commonmark/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/index.js +22 -0
- package/node_modules/micromark-core-commonmark/dev/lib/attention.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/attention.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/attention.js +294 -0
- package/node_modules/micromark-core-commonmark/dev/lib/autolink.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/autolink.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/autolink.js +269 -0
- package/node_modules/micromark-core-commonmark/dev/lib/blank-line.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/blank-line.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/blank-line.js +62 -0
- package/node_modules/micromark-core-commonmark/dev/lib/block-quote.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/block-quote.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/block-quote.js +162 -0
- package/node_modules/micromark-core-commonmark/dev/lib/character-escape.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/character-escape.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/character-escape.js +69 -0
- package/node_modules/micromark-core-commonmark/dev/lib/character-reference.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/character-reference.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/character-reference.js +166 -0
- package/node_modules/micromark-core-commonmark/dev/lib/code-fenced.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/code-fenced.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/code-fenced.js +472 -0
- package/node_modules/micromark-core-commonmark/dev/lib/code-indented.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/code-indented.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/code-indented.js +178 -0
- package/node_modules/micromark-core-commonmark/dev/lib/code-text.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/code-text.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/code-text.js +257 -0
- package/node_modules/micromark-core-commonmark/dev/lib/content.d.ts +7 -0
- package/node_modules/micromark-core-commonmark/dev/lib/content.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/content.js +185 -0
- package/node_modules/micromark-core-commonmark/dev/lib/definition.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/definition.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/definition.js +295 -0
- package/node_modules/micromark-core-commonmark/dev/lib/hard-break-escape.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/hard-break-escape.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/hard-break-escape.js +65 -0
- package/node_modules/micromark-core-commonmark/dev/lib/heading-atx.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/heading-atx.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/heading-atx.js +229 -0
- package/node_modules/micromark-core-commonmark/dev/lib/html-flow.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/html-flow.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/html-flow.js +938 -0
- package/node_modules/micromark-core-commonmark/dev/lib/html-text.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/html-text.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/html-text.js +783 -0
- package/node_modules/micromark-core-commonmark/dev/lib/label-end.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/label-end.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/label-end.js +658 -0
- package/node_modules/micromark-core-commonmark/dev/lib/label-start-image.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/label-start-image.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/label-start-image.js +119 -0
- package/node_modules/micromark-core-commonmark/dev/lib/label-start-link.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/label-start-link.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/label-start-link.js +71 -0
- package/node_modules/micromark-core-commonmark/dev/lib/line-ending.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/line-ending.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/line-ending.js +34 -0
- package/node_modules/micromark-core-commonmark/dev/lib/list.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/list.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/list.js +306 -0
- package/node_modules/micromark-core-commonmark/dev/lib/partial-non-lazy-continuation.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/partial-non-lazy-continuation.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/partial-non-lazy-continuation.js +67 -0
- package/node_modules/micromark-core-commonmark/dev/lib/setext-underline.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/setext-underline.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/setext-underline.js +213 -0
- package/node_modules/micromark-core-commonmark/dev/lib/thematic-break.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/dev/lib/thematic-break.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/dev/lib/thematic-break.js +120 -0
- package/node_modules/micromark-core-commonmark/index.d.ts +23 -0
- package/node_modules/micromark-core-commonmark/index.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/index.js +22 -0
- package/node_modules/micromark-core-commonmark/lib/attention.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/attention.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/attention.js +231 -0
- package/node_modules/micromark-core-commonmark/lib/autolink.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/autolink.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/autolink.js +233 -0
- package/node_modules/micromark-core-commonmark/lib/blank-line.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/blank-line.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/blank-line.js +61 -0
- package/node_modules/micromark-core-commonmark/lib/block-quote.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/block-quote.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/block-quote.js +143 -0
- package/node_modules/micromark-core-commonmark/lib/character-escape.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/character-escape.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/character-escape.js +64 -0
- package/node_modules/micromark-core-commonmark/lib/character-reference.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/character-reference.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/character-reference.js +149 -0
- package/node_modules/micromark-core-commonmark/lib/code-fenced.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/code-fenced.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/code-fenced.js +422 -0
- package/node_modules/micromark-core-commonmark/lib/code-indented.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/code-indented.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/code-indented.js +159 -0
- package/node_modules/micromark-core-commonmark/lib/code-text.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/code-text.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/code-text.js +225 -0
- package/node_modules/micromark-core-commonmark/lib/content.d.ts +7 -0
- package/node_modules/micromark-core-commonmark/lib/content.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/content.js +163 -0
- package/node_modules/micromark-core-commonmark/lib/definition.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/definition.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/definition.js +254 -0
- package/node_modules/micromark-core-commonmark/lib/hard-break-escape.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/hard-break-escape.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/hard-break-escape.js +60 -0
- package/node_modules/micromark-core-commonmark/lib/heading-atx.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/heading-atx.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/heading-atx.js +191 -0
- package/node_modules/micromark-core-commonmark/lib/html-flow.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/html-flow.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/html-flow.js +827 -0
- package/node_modules/micromark-core-commonmark/lib/html-text.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/html-text.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/html-text.js +678 -0
- package/node_modules/micromark-core-commonmark/lib/label-end.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/label-end.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/label-end.js +561 -0
- package/node_modules/micromark-core-commonmark/lib/label-start-image.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/label-start-image.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/label-start-image.js +110 -0
- package/node_modules/micromark-core-commonmark/lib/label-start-link.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/label-start-link.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/label-start-link.js +63 -0
- package/node_modules/micromark-core-commonmark/lib/line-ending.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/line-ending.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/line-ending.js +33 -0
- package/node_modules/micromark-core-commonmark/lib/list.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/list.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/list.js +213 -0
- package/node_modules/micromark-core-commonmark/lib/partial-non-lazy-continuation.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/partial-non-lazy-continuation.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/partial-non-lazy-continuation.js +61 -0
- package/node_modules/micromark-core-commonmark/lib/setext-underline.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/setext-underline.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/setext-underline.js +187 -0
- package/node_modules/micromark-core-commonmark/lib/thematic-break.d.ts +4 -0
- package/node_modules/micromark-core-commonmark/lib/thematic-break.d.ts.map +1 -0
- package/node_modules/micromark-core-commonmark/lib/thematic-break.js +102 -0
- package/node_modules/micromark-core-commonmark/license +22 -0
- package/node_modules/micromark-core-commonmark/package.json +62 -0
- package/node_modules/micromark-core-commonmark/readme.md +171 -0
- package/node_modules/micromark-factory-destination/dev/index.d.ts +42 -0
- package/node_modules/micromark-factory-destination/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-factory-destination/dev/index.js +255 -0
- package/node_modules/micromark-factory-destination/index.d.ts +42 -0
- package/node_modules/micromark-factory-destination/index.d.ts.map +1 -0
- package/node_modules/micromark-factory-destination/index.js +206 -0
- package/node_modules/micromark-factory-destination/license +22 -0
- package/node_modules/micromark-factory-destination/package.json +57 -0
- package/node_modules/micromark-factory-destination/readme.md +234 -0
- package/node_modules/micromark-factory-label/dev/index.d.ts +37 -0
- package/node_modules/micromark-factory-label/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-factory-label/dev/index.js +172 -0
- package/node_modules/micromark-factory-label/index.d.ts +37 -0
- package/node_modules/micromark-factory-label/index.d.ts.map +1 -0
- package/node_modules/micromark-factory-label/index.js +148 -0
- package/node_modules/micromark-factory-label/license +22 -0
- package/node_modules/micromark-factory-label/package.json +60 -0
- package/node_modules/micromark-factory-label/readme.md +224 -0
- package/node_modules/micromark-factory-space/dev/index.d.ts +60 -0
- package/node_modules/micromark-factory-space/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-factory-space/dev/index.js +122 -0
- package/node_modules/micromark-factory-space/index.d.ts +60 -0
- package/node_modules/micromark-factory-space/index.d.ts.map +1 -0
- package/node_modules/micromark-factory-space/index.js +116 -0
- package/node_modules/micromark-factory-space/license +22 -0
- package/node_modules/micromark-factory-space/package.json +46 -0
- package/node_modules/micromark-factory-space/readme.md +225 -0
- package/node_modules/micromark-factory-title/dev/index.d.ts +36 -0
- package/node_modules/micromark-factory-title/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-factory-title/dev/index.js +169 -0
- package/node_modules/micromark-factory-title/index.d.ts +36 -0
- package/node_modules/micromark-factory-title/index.d.ts.map +1 -0
- package/node_modules/micromark-factory-title/index.js +158 -0
- package/node_modules/micromark-factory-title/license +22 -0
- package/node_modules/micromark-factory-title/package.json +58 -0
- package/node_modules/micromark-factory-title/readme.md +229 -0
- package/node_modules/micromark-factory-whitespace/dev/index.d.ts +22 -0
- package/node_modules/micromark-factory-whitespace/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-factory-whitespace/dev/index.js +53 -0
- package/node_modules/micromark-factory-whitespace/index.d.ts +22 -0
- package/node_modules/micromark-factory-whitespace/index.d.ts.map +1 -0
- package/node_modules/micromark-factory-whitespace/index.js +44 -0
- package/node_modules/micromark-factory-whitespace/license +22 -0
- package/node_modules/micromark-factory-whitespace/package.json +57 -0
- package/node_modules/micromark-factory-whitespace/readme.md +205 -0
- package/node_modules/micromark-util-character/dev/index.d.ts +195 -0
- package/node_modules/micromark-util-character/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-util-character/dev/index.js +252 -0
- package/node_modules/micromark-util-character/index.d.ts +195 -0
- package/node_modules/micromark-util-character/index.d.ts.map +1 -0
- package/node_modules/micromark-util-character/index.js +246 -0
- package/node_modules/micromark-util-character/license +22 -0
- package/node_modules/micromark-util-character/package.json +57 -0
- package/node_modules/micromark-util-character/readme.md +446 -0
- package/node_modules/micromark-util-chunked/dev/index.d.ts +41 -0
- package/node_modules/micromark-util-chunked/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-util-chunked/dev/index.js +89 -0
- package/node_modules/micromark-util-chunked/index.d.ts +41 -0
- package/node_modules/micromark-util-chunked/index.d.ts.map +1 -0
- package/node_modules/micromark-util-chunked/index.js +81 -0
- package/node_modules/micromark-util-chunked/license +22 -0
- package/node_modules/micromark-util-chunked/package.json +57 -0
- package/node_modules/micromark-util-chunked/readme.md +219 -0
- package/node_modules/micromark-util-classify-character/dev/index.d.ts +18 -0
- package/node_modules/micromark-util-classify-character/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-util-classify-character/dev/index.js +38 -0
- package/node_modules/micromark-util-classify-character/index.d.ts +18 -0
- package/node_modules/micromark-util-classify-character/index.d.ts.map +1 -0
- package/node_modules/micromark-util-classify-character/index.js +27 -0
- package/node_modules/micromark-util-classify-character/license +22 -0
- package/node_modules/micromark-util-classify-character/package.json +59 -0
- package/node_modules/micromark-util-classify-character/readme.md +205 -0
- package/node_modules/micromark-util-combine-extensions/index.d.ts +22 -0
- package/node_modules/micromark-util-combine-extensions/index.d.ts.map +1 -0
- package/node_modules/micromark-util-combine-extensions/index.js +143 -0
- package/node_modules/micromark-util-combine-extensions/license +22 -0
- package/node_modules/micromark-util-combine-extensions/package.json +52 -0
- package/node_modules/micromark-util-combine-extensions/readme.md +201 -0
- package/node_modules/micromark-util-decode-numeric-character-reference/dev/index.d.ts +16 -0
- package/node_modules/micromark-util-decode-numeric-character-reference/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-util-decode-numeric-character-reference/dev/index.js +42 -0
- package/node_modules/micromark-util-decode-numeric-character-reference/index.d.ts +16 -0
- package/node_modules/micromark-util-decode-numeric-character-reference/index.d.ts.map +1 -0
- package/node_modules/micromark-util-decode-numeric-character-reference/index.js +32 -0
- package/node_modules/micromark-util-decode-numeric-character-reference/license +22 -0
- package/node_modules/micromark-util-decode-numeric-character-reference/package.json +59 -0
- package/node_modules/micromark-util-decode-numeric-character-reference/readme.md +184 -0
- package/node_modules/micromark-util-decode-string/dev/index.d.ts +14 -0
- package/node_modules/micromark-util-decode-string/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-util-decode-string/dev/index.js +53 -0
- package/node_modules/micromark-util-decode-string/index.d.ts +14 -0
- package/node_modules/micromark-util-decode-string/index.d.ts.map +1 -0
- package/node_modules/micromark-util-decode-string/index.js +45 -0
- package/node_modules/micromark-util-decode-string/license +22 -0
- package/node_modules/micromark-util-decode-string/package.json +63 -0
- package/node_modules/micromark-util-decode-string/readme.md +179 -0
- package/node_modules/micromark-util-edit-map/dev/index.d.ts +78 -0
- package/node_modules/micromark-util-edit-map/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-util-edit-map/dev/index.js +237 -0
- package/node_modules/micromark-util-edit-map/index.d.ts +78 -0
- package/node_modules/micromark-util-edit-map/index.d.ts.map +1 -0
- package/node_modules/micromark-util-edit-map/index.js +225 -0
- package/node_modules/micromark-util-edit-map/license +22 -0
- package/node_modules/micromark-util-edit-map/package.json +48 -0
- package/node_modules/micromark-util-edit-map/readme.md +234 -0
- package/node_modules/micromark-util-encode/index.d.ts +14 -0
- package/node_modules/micromark-util-encode/index.d.ts.map +1 -0
- package/node_modules/micromark-util-encode/index.js +33 -0
- package/node_modules/micromark-util-encode/license +22 -0
- package/node_modules/micromark-util-encode/package.json +47 -0
- package/node_modules/micromark-util-encode/readme.md +176 -0
- package/node_modules/micromark-util-html-tag-name/index.d.ts +30 -0
- package/node_modules/micromark-util-html-tag-name/index.d.ts.map +1 -0
- package/node_modules/micromark-util-html-tag-name/index.js +93 -0
- package/node_modules/micromark-util-html-tag-name/license +22 -0
- package/node_modules/micromark-util-html-tag-name/package.json +47 -0
- package/node_modules/micromark-util-html-tag-name/readme.md +193 -0
- package/node_modules/micromark-util-normalize-identifier/dev/index.d.ts +21 -0
- package/node_modules/micromark-util-normalize-identifier/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-util-normalize-identifier/dev/index.js +38 -0
- package/node_modules/micromark-util-normalize-identifier/index.d.ts +21 -0
- package/node_modules/micromark-util-normalize-identifier/index.d.ts.map +1 -0
- package/node_modules/micromark-util-normalize-identifier/index.js +33 -0
- package/node_modules/micromark-util-normalize-identifier/license +22 -0
- package/node_modules/micromark-util-normalize-identifier/package.json +58 -0
- package/node_modules/micromark-util-normalize-identifier/readme.md +187 -0
- package/node_modules/micromark-util-resolve-all/index.d.ts +22 -0
- package/node_modules/micromark-util-resolve-all/index.d.ts.map +1 -0
- package/node_modules/micromark-util-resolve-all/index.js +32 -0
- package/node_modules/micromark-util-resolve-all/license +22 -0
- package/node_modules/micromark-util-resolve-all/package.json +48 -0
- package/node_modules/micromark-util-resolve-all/readme.md +238 -0
- package/node_modules/micromark-util-sanitize-uri/dev/index.d.ts +36 -0
- package/node_modules/micromark-util-sanitize-uri/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-util-sanitize-uri/dev/index.js +124 -0
- package/node_modules/micromark-util-sanitize-uri/index.d.ts +36 -0
- package/node_modules/micromark-util-sanitize-uri/index.d.ts.map +1 -0
- package/node_modules/micromark-util-sanitize-uri/index.js +107 -0
- package/node_modules/micromark-util-sanitize-uri/license +22 -0
- package/node_modules/micromark-util-sanitize-uri/package.json +59 -0
- package/node_modules/micromark-util-sanitize-uri/readme.md +214 -0
- package/node_modules/micromark-util-subtokenize/dev/index.d.ts +12 -0
- package/node_modules/micromark-util-subtokenize/dev/index.d.ts.map +1 -0
- package/node_modules/micromark-util-subtokenize/dev/index.js +284 -0
- package/node_modules/micromark-util-subtokenize/dev/lib/splice-buffer.d.ts +161 -0
- package/node_modules/micromark-util-subtokenize/dev/lib/splice-buffer.d.ts.map +1 -0
- package/node_modules/micromark-util-subtokenize/dev/lib/splice-buffer.js +288 -0
- package/node_modules/micromark-util-subtokenize/index.d.ts +12 -0
- package/node_modules/micromark-util-subtokenize/index.d.ts.map +1 -0
- package/node_modules/micromark-util-subtokenize/index.js +228 -0
- package/node_modules/micromark-util-subtokenize/lib/splice-buffer.d.ts +161 -0
- package/node_modules/micromark-util-subtokenize/lib/splice-buffer.d.ts.map +1 -0
- package/node_modules/micromark-util-subtokenize/lib/splice-buffer.js +251 -0
- package/node_modules/micromark-util-subtokenize/license +22 -0
- package/node_modules/micromark-util-subtokenize/package.json +60 -0
- package/node_modules/micromark-util-subtokenize/readme.md +181 -0
- package/node_modules/micromark-util-symbol/lib/codes.d.ts +139 -0
- package/node_modules/micromark-util-symbol/lib/codes.d.ts.map +1 -0
- package/node_modules/micromark-util-symbol/lib/codes.js +158 -0
- package/node_modules/micromark-util-symbol/lib/constants.d.ts +37 -0
- package/node_modules/micromark-util-symbol/lib/constants.d.ts.map +1 -0
- package/node_modules/micromark-util-symbol/lib/constants.js +44 -0
- package/node_modules/micromark-util-symbol/lib/default.d.ts +5 -0
- package/node_modules/micromark-util-symbol/lib/default.d.ts.map +1 -0
- package/node_modules/micromark-util-symbol/lib/default.js +4 -0
- package/node_modules/micromark-util-symbol/lib/types.d.ts +106 -0
- package/node_modules/micromark-util-symbol/lib/types.d.ts.map +1 -0
- package/node_modules/micromark-util-symbol/lib/types.js +453 -0
- package/node_modules/micromark-util-symbol/lib/values.d.ts +102 -0
- package/node_modules/micromark-util-symbol/lib/values.d.ts.map +1 -0
- package/node_modules/micromark-util-symbol/lib/values.js +109 -0
- package/node_modules/micromark-util-symbol/license +22 -0
- package/node_modules/micromark-util-symbol/package.json +43 -0
- package/node_modules/micromark-util-symbol/readme.md +168 -0
- package/node_modules/micromark-util-types/index.d.ts +1315 -0
- package/node_modules/micromark-util-types/index.js +2 -0
- package/node_modules/micromark-util-types/license +22 -0
- package/node_modules/micromark-util-types/package.json +37 -0
- package/node_modules/micromark-util-types/readme.md +151 -0
- package/node_modules/smol-toml/LICENSE +24 -0
- package/node_modules/smol-toml/README.md +605 -0
- package/node_modules/smol-toml/dist/date.d.ts +41 -0
- package/node_modules/smol-toml/dist/date.js +156 -0
- package/node_modules/smol-toml/dist/error.d.ts +37 -0
- package/node_modules/smol-toml/dist/error.js +67 -0
- package/node_modules/smol-toml/dist/extract.js +289 -0
- package/node_modules/smol-toml/dist/index.cjs +770 -0
- package/node_modules/smol-toml/dist/index.d.ts +44 -0
- package/node_modules/smol-toml/dist/index.js +34 -0
- package/node_modules/smol-toml/dist/parse.d.ts +73 -0
- package/node_modules/smol-toml/dist/parse.js +164 -0
- package/node_modules/smol-toml/dist/primitive.js +151 -0
- package/node_modules/smol-toml/dist/stringify.d.ts +32 -0
- package/node_modules/smol-toml/dist/stringify.js +219 -0
- package/node_modules/smol-toml/dist/struct.js +156 -0
- package/node_modules/smol-toml/dist/util.d.ts +43 -0
- package/node_modules/smol-toml/dist/util.js +57 -0
- package/node_modules/smol-toml/package.json +68 -0
- package/node_modules/unist-util-stringify-position/index.d.ts +1 -0
- package/node_modules/unist-util-stringify-position/index.js +1 -0
- package/node_modules/unist-util-stringify-position/lib/index.d.ts +61 -0
- package/node_modules/unist-util-stringify-position/lib/index.js +84 -0
- package/node_modules/unist-util-stringify-position/license +22 -0
- package/node_modules/unist-util-stringify-position/package.json +80 -0
- package/node_modules/unist-util-stringify-position/readme.md +206 -0
- package/package.json +27 -8
- package/scripts/prepare-github-keyring.js +29 -0
- package/src/codex-app.js +20 -2
- package/src/codex-pricing.js +79 -0
- package/src/codex-projects.js +90 -0
- package/src/codex-session-scanner.js +748 -0
- package/src/codex-session-usage.js +332 -0
- package/src/dashboard/app.js +2 -2
- package/src/dashboard/dist/app.js +135 -60
- package/src/dashboard/dist/app.js.map +1 -1
- package/src/dashboard/dist/styles.css +1 -1
- package/src/dashboard/react/components/AnimatedUsage.test.tsx +78 -0
- package/src/dashboard/react/components/CardStatusIcons.test.tsx +146 -0
- package/src/dashboard/react/components/CardStatusIcons.tsx +138 -0
- package/src/dashboard/react/components/ImageGallery.tsx +22 -0
- package/src/dashboard/react/components/ModelSettings.test.tsx +4 -4
- package/src/dashboard/react/components/ModelSettings.tsx +1 -1
- package/src/dashboard/react/components/OpenChatButton.test.tsx +19 -0
- package/src/dashboard/react/components/OpenChatButton.tsx +2 -1
- package/src/dashboard/react/components/PrGlyph.tsx +35 -0
- package/src/dashboard/react/components/PullRequestBadges.tsx +18 -0
- package/src/dashboard/react/components/QueuedPrompts.test.tsx +42 -0
- package/src/dashboard/react/components/QueuedPrompts.tsx +34 -0
- package/src/dashboard/react/components/RelativeTime.test.tsx +100 -0
- package/src/dashboard/react/components/RelativeTime.tsx +46 -11
- package/src/dashboard/react/components/ReplyCover.test.tsx +30 -0
- package/src/dashboard/react/components/ReplyCover.tsx +33 -0
- package/src/dashboard/react/components/ReplyImagePreview.test.tsx +90 -0
- package/src/dashboard/react/components/ReplyImagePreview.tsx +34 -0
- package/src/dashboard/react/components/ReplyMarkdown.test.tsx +108 -0
- package/src/dashboard/react/components/ReplyMarkdown.tsx +112 -0
- package/src/dashboard/react/components/SavedUsageFormat.test.tsx +11 -0
- package/src/dashboard/react/components/SavedUsageFormat.tsx +68 -0
- package/src/dashboard/react/components/StatusBadge.tsx +2 -2
- package/src/dashboard/react/components/TaskBoard.test.tsx +256 -1
- package/src/dashboard/react/components/TaskBoard.tsx +136 -28
- package/src/dashboard/react/components/TaskCard.tsx +7 -4
- package/src/dashboard/react/components/TaskDetail.test.tsx +92 -1
- package/src/dashboard/react/components/TaskDetail.tsx +37 -10
- package/src/dashboard/react/components/TurnContent.test.tsx +44 -0
- package/src/dashboard/react/components/TurnContent.tsx +53 -0
- package/src/dashboard/react/components/UsagePanel.tsx +2 -2
- package/src/dashboard/react/image-cache.test.ts +23 -0
- package/src/dashboard/react/image-cache.ts +33 -0
- package/src/dashboard/react/presentation.test.tsx +31 -0
- package/src/dashboard/react/styles.css +126 -2
- package/src/dashboard/react/types.ts +55 -1
- package/src/dashboard/time.js +39 -0
- package/src/dashboard.js +4 -3
- package/src/data/codex-pricing.json +537 -0
- package/src/mcp-app/dist/index.html +216 -112
- package/src/mcp-app/react/AppearancePicker.test.tsx +50 -0
- package/src/mcp-app/react/AppearancePicker.tsx +56 -0
- package/src/mcp-app/react/GitHubConnection.test.tsx +78 -0
- package/src/mcp-app/react/GitHubConnection.tsx +78 -0
- package/src/mcp-app/react/NextNotificationCenter.test.tsx +121 -0
- package/src/mcp-app/react/NextNotificationCenter.tsx +94 -0
- package/src/mcp-app/react/NextTaskDetail.test.tsx +124 -0
- package/src/mcp-app/react/NextTaskDetail.tsx +147 -0
- package/src/mcp-app/react/ProjectPicker.tsx +49 -0
- package/src/mcp-app/react/main.test.tsx +499 -49
- package/src/mcp-app/react/main.tsx +359 -86
- package/src/mcp-app/react/styles.css +186 -9
- package/src/mcp-app/react/themes.test.ts +34 -0
- package/src/mcp-app/react/themes.ts +112 -0
- package/src/mcp-app.js +238 -120
- package/src/mcp.js +8 -1
- package/src/next-cache.js +16 -0
- package/src/next-github.js +273 -0
- package/src/next-notifications.js +90 -0
- package/src/recorded-usage-tracker.js +92 -0
- package/src/reply-image.js +65 -0
- package/src/usage-tracker.js +7 -6
- package/src/usage.js +7 -355
- package/src/user-prompt.js +55 -0
- package/src/version.js +0 -1
- package/node_modules/proper-lockfile/CHANGELOG.md +0 -108
- package/node_modules/which/CHANGELOG.md +0 -166
- package/src/resolve-ccusage.js +0 -17
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import { migrateCache } from "./next-cache.js";
|
|
2
|
+
import { filterTasks } from "./dashboard/state.js";
|
|
3
|
+
import { mkdir, readFile, rm } from "node:fs/promises";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { acquireWorkspaceLock } from "./workspace.js";
|
|
6
|
+
import { writeDurableAtomic } from "./state-store.js";
|
|
7
|
+
|
|
8
|
+
const DEVICE_URL = "https://github.com/login/device";
|
|
9
|
+
const CACHE_MS = 60_000;
|
|
10
|
+
const displayPullRequests = task => task.pullRequests?.length ? task.pullRequests : task.earlierPullRequest ? [task.earlierPullRequest] : [];
|
|
11
|
+
export function pullRequestIdentity(url) {
|
|
12
|
+
const match = /^https:\/\/github\.com\/([A-Za-z0-9][A-Za-z0-9-]*)\/([A-Za-z0-9_.-]+)\/pull\/([1-9]\d*)\/?$/.exec(url);
|
|
13
|
+
if (!match || !Number.isSafeInteger(Number(match[3]))) return null;
|
|
14
|
+
return { owner: match[1], repo: match[2], number: Number(match[3]), url: `https://github.com/${match[1]}/${match[2]}/pull/${match[3]}` };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// The native library keeps secrets in Keychain, Credential Manager, or Secret Service.
|
|
18
|
+
// It is loaded only for sign-in; an unavailable credential store never falls back to a file.
|
|
19
|
+
export class GitHubCredentials {
|
|
20
|
+
async entry(clientId) {
|
|
21
|
+
const { AsyncEntry } = await import("@napi-rs/keyring");
|
|
22
|
+
return new AsyncEntry("TaskChef Next GitHub", clientId, { linux: { store: "secret-service" } });
|
|
23
|
+
}
|
|
24
|
+
async read(clientId) { const value = await (await this.entry(clientId)).getPassword(); return value ? JSON.parse(value) : {}; }
|
|
25
|
+
async write(clientId, value) { await (await this.entry(clientId)).setPassword(JSON.stringify(value)); }
|
|
26
|
+
async remove(clientId) { await (await this.entry(clientId)).deletePassword(); }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class NextGitHub {
|
|
30
|
+
constructor({ stateDir, credentials = new GitHubCredentials(), fetch = globalThis.fetch, now = Date.now } = {}) {
|
|
31
|
+
this.stateDir = stateDir; this.credentials = credentials; this.fetch = fetch; this.now = now;
|
|
32
|
+
this.cache = new Map(); this.retryUntil = 0;
|
|
33
|
+
}
|
|
34
|
+
async locked(operation) {
|
|
35
|
+
await mkdir(join(this.stateDir, "github-auth"), { recursive: true, mode: 0o700 });
|
|
36
|
+
const release = await acquireWorkspaceLock(join(this.stateDir, "github-auth"));
|
|
37
|
+
try { return await operation(); } finally { await release(); }
|
|
38
|
+
}
|
|
39
|
+
get cachePath() { return join(this.stateDir, "cache", "github-prs.json"); }
|
|
40
|
+
async clearCache() {
|
|
41
|
+
this.cache.clear();
|
|
42
|
+
await rm(this.cachePath, { force: true });
|
|
43
|
+
await rm(join(this.stateDir, "github-auth", "pr-cache.json"), { force: true });
|
|
44
|
+
}
|
|
45
|
+
async loadCache(clientId, login) {
|
|
46
|
+
this.cache.clear();
|
|
47
|
+
try {
|
|
48
|
+
await migrateCache(join(this.stateDir, "github-auth", "pr-cache.json"), this.cachePath);
|
|
49
|
+
const saved = JSON.parse(await readFile(this.cachePath, "utf8"));
|
|
50
|
+
if (saved.version !== 1 || typeof saved.entries !== "object" || !saved.entries || Array.isArray(saved.entries)) throw new Error();
|
|
51
|
+
if (saved.clientId !== clientId || saved.login !== login) return;
|
|
52
|
+
for (const [url, entry] of Object.entries(saved.entries)) {
|
|
53
|
+
if (!pullRequestIdentity(url) || entry?.pr?.url !== url || !Number.isFinite(entry.expiresAt)
|
|
54
|
+
|| !["unknown", "open", "draft", "closed", "merged"].includes(entry.pr.state)
|
|
55
|
+
|| !["unknown", "none", "passed", "failed", "pending"].includes(entry.pr.checks)
|
|
56
|
+
|| !entry.turns || typeof entry.turns !== "object" || Array.isArray(entry.turns)
|
|
57
|
+
|| Object.values(entry.turns).some(value => typeof value !== "string")) throw new Error();
|
|
58
|
+
this.cache.set(url, entry);
|
|
59
|
+
}
|
|
60
|
+
} catch (error) {
|
|
61
|
+
if (error.code !== "ENOENT") throw new Error("TaskChef cannot read its saved PR cache. Remove cache/github-prs.json and refresh.");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async saveCache(clientId, login) {
|
|
65
|
+
try { await writeDurableAtomic(this.cachePath, JSON.stringify({ version: 1, clientId, login, entries: Object.fromEntries(this.cache) })); }
|
|
66
|
+
catch { throw new Error("TaskChef cannot save its PR cache. Check local file access and refresh."); }
|
|
67
|
+
}
|
|
68
|
+
async request(url, { token, form, query } = {}) {
|
|
69
|
+
if (this.retryUntil > this.now()) throw new Error("GitHub is rate limited. Try again later.");
|
|
70
|
+
let response;
|
|
71
|
+
try {
|
|
72
|
+
response = await this.fetch(url, { method: form || query ? "POST" : "GET", redirect: "error", signal: AbortSignal.timeout(10_000), headers: {
|
|
73
|
+
Accept: "application/json", "User-Agent": "TaskChef-Next", ...(token ? { Authorization: `Bearer ${token}` } : {}),
|
|
74
|
+
...(query ? { "Content-Type": "application/json" } : {}),
|
|
75
|
+
}, ...(form ? { body: new URLSearchParams(form) } : query ? { body: JSON.stringify({ query }) } : {}) });
|
|
76
|
+
} catch { throw new Error("GitHub could not be reached. Try again."); }
|
|
77
|
+
if (!response.ok) {
|
|
78
|
+
const error = new Error(response.status === 401 ? "GitHub sign-in expired. Sign in again." : response.status === 403 || response.status === 429 ? "GitHub access is blocked or rate limited. Check repository access and try later." : "GitHub could not return PR status.");
|
|
79
|
+
error.status = response.status;
|
|
80
|
+
let rateLimited = response.status === 429;
|
|
81
|
+
if (response.status === 403) {
|
|
82
|
+
const body = await response.json().catch(() => null);
|
|
83
|
+
if (/^Resource not accessible by (integration|personal access token)$/i.test(body?.message ?? "")) error.accessIssue = "denied";
|
|
84
|
+
rateLimited = !error.accessIssue && (/rate limit/i.test(body?.message ?? "") || response.headers?.get("x-ratelimit-remaining") === "0" || !!response.headers?.get("retry-after"));
|
|
85
|
+
}
|
|
86
|
+
if (rateLimited) {
|
|
87
|
+
const retry = Number(response.headers?.get("retry-after"));
|
|
88
|
+
this.retryUntil = this.now() + Math.max(CACHE_MS, Number.isFinite(retry) ? retry * 1000 : CACHE_MS);
|
|
89
|
+
}
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
try { return await response.json(); } catch { throw new Error("GitHub returned an invalid response."); }
|
|
93
|
+
}
|
|
94
|
+
async checks(identity, sha, token) {
|
|
95
|
+
if (!/^[a-f0-9]{40,64}$/i.test(sha ?? "")) return { checks: "unknown", error: "CI status unavailable: GitHub did not return the head revision." };
|
|
96
|
+
const base = `https://api.github.com/repos/${identity.owner}/${identity.repo}/commits/${sha}`;
|
|
97
|
+
try {
|
|
98
|
+
const [runs, statuses] = await Promise.all([
|
|
99
|
+
this.request(`${base}/check-runs?filter=latest&per_page=100`, { token }),
|
|
100
|
+
this.request(`${base}/status?per_page=100`, { token }),
|
|
101
|
+
]);
|
|
102
|
+
if (!Array.isArray(runs.check_runs) || !Number.isFinite(runs.total_count) || !Number.isFinite(statuses.total_count)) throw new Error("Invalid CI response.");
|
|
103
|
+
// Never report success when a page omits checks we have not inspected.
|
|
104
|
+
if (runs.total_count > runs.check_runs.length) return { checks: "unknown", error: "CI status unavailable: more than 100 checks. Open GitHub for the full result." };
|
|
105
|
+
const conclusions = runs.check_runs.filter(run => run.status === "completed").map(run => run.conclusion);
|
|
106
|
+
const failed = conclusions.some(value => ["failure", "cancelled", "timed_out", "action_required", "startup_failure", "stale"].includes(value)) || (statuses.total_count > 0 && ["failure", "error"].includes(statuses.state));
|
|
107
|
+
const pending = runs.check_runs.some(run => run.status !== "completed") || (statuses.total_count > 0 && statuses.state === "pending");
|
|
108
|
+
const unknown = conclusions.some(value => !["success", "neutral", "skipped", "failure", "cancelled", "timed_out", "action_required", "startup_failure", "stale"].includes(value)) || (statuses.total_count > 0 && !["success", "pending", "failure", "error"].includes(statuses.state));
|
|
109
|
+
return { checks: failed ? "failed" : unknown ? "unknown" : pending ? "pending" : runs.total_count + statuses.total_count === 0 ? "none" : "passed" };
|
|
110
|
+
} catch (error) {
|
|
111
|
+
if (error.status === 401) throw error;
|
|
112
|
+
return { checks: "unknown", ...(error.accessIssue ? { accessIssue: error.accessIssue } : {}), error: "CI status unavailable. Check GitHub repository access and try Refresh." };
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async readCredential(clientId) {
|
|
116
|
+
try {
|
|
117
|
+
const value = await this.credentials.read(clientId);
|
|
118
|
+
if (!value || typeof value !== "object" || Array.isArray(value)
|
|
119
|
+
|| (value.token && (typeof value.token !== "string" || typeof value.login !== "string"))
|
|
120
|
+
|| (value.refreshToken !== undefined && typeof value.refreshToken !== "string")
|
|
121
|
+
|| (value.expiresAt != null && !Number.isFinite(value.expiresAt))
|
|
122
|
+
|| (value.pending && (typeof value.pending.deviceCode !== "string" || typeof value.pending.userCode !== "string"
|
|
123
|
+
|| ![value.pending.expiresAt, value.pending.interval, value.pending.nextPoll].every(Number.isFinite)))) throw new Error();
|
|
124
|
+
return value;
|
|
125
|
+
} catch { throw new Error("Cannot read the system credential store. Unlock it and try again."); }
|
|
126
|
+
}
|
|
127
|
+
async save(clientId, value) {
|
|
128
|
+
try { await this.credentials.write(clientId, value); }
|
|
129
|
+
catch { throw new Error("Cannot save GitHub sign-in in the system credential store. No token was saved to a file."); }
|
|
130
|
+
}
|
|
131
|
+
publicAuth(value, configured = true) {
|
|
132
|
+
return { configured, connected: Boolean(value.token), login: value.login ?? null,
|
|
133
|
+
...(value.pending ? { pending: { userCode: value.pending.userCode, verificationUrl: DEVICE_URL, expiresAt: value.pending.expiresAt } } : {}) };
|
|
134
|
+
}
|
|
135
|
+
async tokenBundle(clientId, value) {
|
|
136
|
+
if (!value.token || !value.expiresAt || value.expiresAt > this.now() + 30_000) return value;
|
|
137
|
+
if (!value.refreshToken) throw new Error("GitHub sign-in expired. Sign in again.");
|
|
138
|
+
const result = await this.request("https://github.com/login/oauth/access_token", { form: { client_id: clientId, grant_type: "refresh_token", refresh_token: value.refreshToken } });
|
|
139
|
+
if (result.error || typeof result.access_token !== "string" || typeof result.refresh_token !== "string" || !Number.isFinite(result.expires_in)) { await this.save(clientId, {}); throw new Error("GitHub sign-in expired. Sign in again."); }
|
|
140
|
+
const next = { token: result.access_token, refreshToken: result.refresh_token, expiresAt: this.now() + result.expires_in * 1000, login: value.login };
|
|
141
|
+
await this.save(clientId, next);
|
|
142
|
+
return next;
|
|
143
|
+
}
|
|
144
|
+
async auth(clientId, action = "status") {
|
|
145
|
+
if (!clientId) {
|
|
146
|
+
if (action !== "status") throw new Error("Register the GitHub App and set its public client ID in plugin settings first.");
|
|
147
|
+
return { configured: false, connected: false, login: null };
|
|
148
|
+
}
|
|
149
|
+
return this.locked(async () => {
|
|
150
|
+
let value = await this.readCredential(clientId);
|
|
151
|
+
if (action === "disconnect") {
|
|
152
|
+
try { await this.credentials.remove(clientId); } catch { throw new Error("Cannot remove GitHub sign-in from the system credential store."); }
|
|
153
|
+
await this.clearCache();
|
|
154
|
+
return this.publicAuth({});
|
|
155
|
+
}
|
|
156
|
+
if (action === "start") {
|
|
157
|
+
const result = await this.request("https://github.com/login/device/code", { form: { client_id: clientId, scope: "offline_access" } });
|
|
158
|
+
if (result.error || typeof result.device_code !== "string" || typeof result.user_code !== "string" || !Number.isFinite(result.expires_in) || !Number.isFinite(result.interval)) throw new Error("GitHub device sign-in is unavailable. Check the app's client ID and enable device flow.");
|
|
159
|
+
value = { pending: { deviceCode: result.device_code, userCode: result.user_code, expiresAt: this.now() + result.expires_in * 1000, interval: Math.max(5, result.interval) * 1000, nextPoll: this.now() + Math.max(5, result.interval) * 1000 } };
|
|
160
|
+
await this.save(clientId, value);
|
|
161
|
+
await this.clearCache();
|
|
162
|
+
} else if (action === "poll" && value.pending) {
|
|
163
|
+
const pending = value.pending;
|
|
164
|
+
if (pending.expiresAt <= this.now()) { await this.save(clientId, {}); throw new Error("GitHub sign-in code expired. Start again."); }
|
|
165
|
+
if (pending.nextPoll <= this.now()) {
|
|
166
|
+
// Persist the deadline before requesting, so another MCP process cannot poll too soon.
|
|
167
|
+
pending.nextPoll = this.now() + pending.interval;
|
|
168
|
+
await this.save(clientId, value);
|
|
169
|
+
const result = await this.request("https://github.com/login/oauth/access_token", { form: { client_id: clientId, device_code: pending.deviceCode, grant_type: "urn:ietf:params:oauth:grant-type:device_code" } });
|
|
170
|
+
if (result.error === "slow_down") { pending.interval += 5000; pending.nextPoll = this.now() + pending.interval; await this.save(clientId, value); }
|
|
171
|
+
else if (result.error === "authorization_pending") { /* The user has not approved yet. */ }
|
|
172
|
+
else if (result.error || typeof result.access_token !== "string") { await this.save(clientId, {}); throw new Error("GitHub sign-in was denied or expired. Start again."); }
|
|
173
|
+
else {
|
|
174
|
+
const user = await this.request("https://api.github.com/user", { token: result.access_token });
|
|
175
|
+
if (typeof user.login !== "string") throw new Error("GitHub could not verify your account.");
|
|
176
|
+
value = { token: result.access_token, refreshToken: result.refresh_token, expiresAt: result.expires_in ? this.now() + result.expires_in * 1000 : null, login: user.login };
|
|
177
|
+
await this.save(clientId, value);
|
|
178
|
+
await this.clearCache();
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return this.publicAuth(value);
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
async enrich(snapshot, settings, { force = false, scope = {}, detail = false } = {}) {
|
|
186
|
+
if (!snapshot.healthy) return { snapshot, auth: { configured: Boolean(settings.githubClientId), connected: false, login: null } };
|
|
187
|
+
const clientId = settings.githubClientId;
|
|
188
|
+
const requestedIds = scope.taskIds === undefined ? null : new Set(scope.taskIds);
|
|
189
|
+
const scopedTasks = filterTasks(snapshot.tasks, { date: scope.date ?? "all", now: this.now() }).filter((task) => (!requestedIds || requestedIds.has(task.id)) && (!scope.project || (task.project?.id || task.project?.path || task.project?.name) === scope.project) && (detail || !task.observed.archive && (settings.showCli || task.observed.source !== "cli") && (settings.showExec || task.observed.source !== "exec")));
|
|
190
|
+
const urls = [...new Set(scopedTasks.flatMap(task => displayPullRequests(task).map(pr => pr.url)))];
|
|
191
|
+
const turnsFor = url => Object.fromEntries(scopedTasks.filter(task => displayPullRequests(task).some(pr => pr.url === url)).map(task => [task.id, JSON.stringify([task.turnId ?? task.turnRef, task.observed.lastTurnEvent])]));
|
|
192
|
+
let auth = { configured: Boolean(clientId), connected: false, login: null };
|
|
193
|
+
if (clientId) {
|
|
194
|
+
try {
|
|
195
|
+
await this.locked(async () => {
|
|
196
|
+
let value = await this.readCredential(clientId);
|
|
197
|
+
await this.loadCache(clientId, value.login);
|
|
198
|
+
auth = this.publicAuth(value);
|
|
199
|
+
if (!value.token) { await this.clearCache(); return; }
|
|
200
|
+
value = await this.tokenBundle(clientId, value);
|
|
201
|
+
const needed = urls.filter((url) => pullRequestIdentity(url) && (() => {
|
|
202
|
+
const entry = this.cache.get(url);
|
|
203
|
+
return force || !entry || !detail && (Object.entries(turnsFor(url)).some(([id, turn]) => entry.turns[id] !== turn)
|
|
204
|
+
|| ((entry.pr.state === "unknown" || entry.pr.checks === "pending" || entry.pr.checks === "unknown" || (["open", "draft"].includes(entry.pr.state) && (entry.pr.mergeState === "UNKNOWN" || entry.pr.mergeState == null || entry.pr.mergeable === "UNKNOWN"))) && entry.expiresAt <= this.now()));
|
|
205
|
+
})())
|
|
206
|
+
.sort((a, b) => (this.cache.get(a)?.expiresAt ?? 0) - (this.cache.get(b)?.expiresAt ?? 0)).slice(0, 25);
|
|
207
|
+
for (let start = 0; start < needed.length; start += 25) {
|
|
208
|
+
const batch = needed.slice(start, start + 25);
|
|
209
|
+
const query = `query { ${batch.map((url, i) => {
|
|
210
|
+
const p = pullRequestIdentity(url);
|
|
211
|
+
return `p${i}: repository(owner:${JSON.stringify(p.owner)},name:${JSON.stringify(p.repo)}) { pullRequest(number:${p.number}) { title state merged isDraft headRefOid mergeable mergeStateStatus } }`;
|
|
212
|
+
}).join(" ")} }`;
|
|
213
|
+
let result;
|
|
214
|
+
try { result = await this.request("https://api.github.com/graphql", { token: value.token, query }); }
|
|
215
|
+
catch (error) {
|
|
216
|
+
for (const url of batch) this.cache.set(url, { expiresAt: this.now() + CACHE_MS, turns: { ...this.cache.get(url)?.turns, ...turnsFor(url) }, pr: { url, state: "unknown", checks: "unknown", ...(error.accessIssue ? { accessIssue: error.accessIssue } : {}), error: error.message } });
|
|
217
|
+
auth = { ...auth, error: error.message };
|
|
218
|
+
if (error.status === 401) { await this.save(clientId, {}); auth.connected = false; await this.clearCache(); break; }
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
const ciResults = [];
|
|
222
|
+
// Bound CI requests so one batch does not wait for every PR in sequence.
|
|
223
|
+
for (let offset = 0; offset < batch.length; offset += 5) {
|
|
224
|
+
ciResults.push(...await Promise.all(batch.slice(offset, offset + 5).map(async (url, index) => {
|
|
225
|
+
const p = result.data?.[`p${offset + index}`]?.pullRequest;
|
|
226
|
+
if (!p || !["OPEN", "CLOSED", "MERGED"].includes(p.state)) return { checks: "unknown" };
|
|
227
|
+
try { return await this.checks(pullRequestIdentity(url), p.headRefOid, value.token); }
|
|
228
|
+
catch (error) { return { authError: error }; }
|
|
229
|
+
})));
|
|
230
|
+
if (ciResults.some(ci => ci.authError)) break;
|
|
231
|
+
}
|
|
232
|
+
const authError = ciResults.find(ci => ci.authError)?.authError;
|
|
233
|
+
if (authError) {
|
|
234
|
+
if (authError.status === 401) { await this.save(clientId, {}); auth.connected = false; await this.clearCache(); break; }
|
|
235
|
+
throw authError;
|
|
236
|
+
}
|
|
237
|
+
for (const [i, url] of batch.entries()) {
|
|
238
|
+
const p = result.data?.[`p${i}`]?.pullRequest;
|
|
239
|
+
const accessErrors = (result.errors ?? []).filter(error => error.path?.[0] === `p${i}`);
|
|
240
|
+
const accessIssue = accessErrors.some(error => error.type === "FORBIDDEN") ? "denied"
|
|
241
|
+
: !p && accessErrors.some(error => error.type === "NOT_FOUND") ? "not_found" : null;
|
|
242
|
+
const failed = !p || !["OPEN", "CLOSED", "MERGED"].includes(p.state);
|
|
243
|
+
const state = failed || !p ? "unknown" : p.merged === true || p.state === "MERGED" ? "merged" : p.state === "CLOSED" ? "closed" : p.state === "OPEN" ? p.isDraft ? "draft" : "open" : "unknown";
|
|
244
|
+
const ci = ciResults[i];
|
|
245
|
+
const { checks } = ci;
|
|
246
|
+
this.cache.set(url, { expiresAt: this.now() + CACHE_MS, turns: { ...this.cache.get(url)?.turns, ...turnsFor(url) }, pr: { url, state, checks, ...(accessIssue || ci.accessIssue ? { accessIssue: accessIssue || ci.accessIssue } : {}), ...(ci.error ? { error: ci.error } : {}), ...(typeof p?.title === "string" ? { title: p.title } : {}), ...(typeof p?.headRefOid === "string" ? { headRevision: p.headRefOid } : {}), mergeState: p?.mergeStateStatus ?? null, mergeable: p?.mergeable ?? "UNKNOWN", hasMergeConflicts: p?.mergeable === "CONFLICTING" || p?.mergeStateStatus === "DIRTY", canMerge: p?.mergeable === "MERGEABLE" && ["CLEAN", "HAS_HOOKS"].includes(p?.mergeStateStatus) && !p?.isDraft, checkedAt: new Date(this.now()).toISOString(), ...(state === "unknown" ? { error: "PR status unavailable. Check the app's repository access." } : {}) } });
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
if (needed.length && auth.connected) await this.saveCache(clientId, value.login);
|
|
250
|
+
});
|
|
251
|
+
} catch (error) { auth = { ...auth, connected: false, error: error.message }; this.cache.clear(); }
|
|
252
|
+
} else this.cache.clear();
|
|
253
|
+
const tasks = snapshot.tasks.map((task) => {
|
|
254
|
+
const pullRequests = (task.pullRequests ?? []).map((pr) => this.cache.get(pr.url)?.pr ?? { ...pr, state: "unknown", checks: "unknown", error: pullRequestIdentity(pr.url) ? auth.connected ? "Waiting for the next GitHub check." : "Connect GitHub to read PR status." : "This GitHub host or PR URL is not supported." });
|
|
255
|
+
if (task.earlierPullRequest) task = { ...task, earlierPullRequest: this.cache.get(task.earlierPullRequest.url)?.pr ?? { ...task.earlierPullRequest, state: "unknown", checks: "unknown" } };
|
|
256
|
+
if (detail || task.observed.lastTurnEvent !== "completed" || task.observed.archive || task.manualDone) return { ...task, pullRequests };
|
|
257
|
+
// A routine completion returns to its schedule regardless of GitHub access or PR state.
|
|
258
|
+
if (task.scheduled && (task.inputSource === "scheduled" || task.manualScheduled)) return {
|
|
259
|
+
...task, pullRequests, status: "scheduled", statusLabel: "Scheduled",
|
|
260
|
+
summary: task.manualScheduled ? "Latest human turn acknowledged in TaskChef; an active schedule remains." : "Latest scheduled turn ended; an active schedule remains.",
|
|
261
|
+
};
|
|
262
|
+
if (!pullRequests.length) return { ...task, pullRequests };
|
|
263
|
+
const allMerged = pullRequests.every((pr) => pr.state === "merged");
|
|
264
|
+
const status = allMerged ? task.scheduled ? "scheduled" : "completed" : "needs_input";
|
|
265
|
+
return { ...task, pullRequests, status,
|
|
266
|
+
statusLabel: status === "scheduled" ? "Scheduled" : status === "completed" ? "Done" : "Waiting for input/review",
|
|
267
|
+
summary: allMerged ? task.scheduled ? "Latest-turn PRs are merged; an active schedule remains." : "Latest-turn PRs are merged."
|
|
268
|
+
: "Latest-turn PRs need review or confirmed merge status.",
|
|
269
|
+
};
|
|
270
|
+
});
|
|
271
|
+
return { snapshot: { ...snapshot, tasks }, auth };
|
|
272
|
+
}
|
|
273
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { mkdir, readFile } from "node:fs/promises";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { acquireWorkspaceLock } from "./workspace.js";
|
|
6
|
+
import { writeDurableAtomic } from "./state-store.js";
|
|
7
|
+
|
|
8
|
+
const itemSchema = z.object({ id: z.string(), taskId: z.string().nullable(), title: z.string(), detail: z.string(), kind: z.enum(["ready", "interrupted", "confirmation", "error"]), read: z.boolean(), timestamp: z.string() });
|
|
9
|
+
const schema = z.object({ version: z.literal(1), revision: z.number().int().nonnegative(), initialized: z.boolean(), observedAt: z.number().nonnegative().default(0), chats: z.record(z.string(), z.object({ turn: z.string().nullable(), status: z.string().nullable(), announced: z.array(z.string()) })), items: z.array(itemSchema) });
|
|
10
|
+
const empty = () => ({ version: 1, revision: 0, initialized: false, observedAt: 0, chats: {}, items: [] });
|
|
11
|
+
const publicState = (state) => ({ revision: state.revision, items: state.items });
|
|
12
|
+
|
|
13
|
+
// Each operation locks and rereads the shared file. No process owns a stale copy.
|
|
14
|
+
export class NextNotifications {
|
|
15
|
+
constructor(path, { now = () => new Date().toISOString(), clock = () => performance.timeOrigin + performance.now() } = {}) { this.path = path; this.now = now; this.clock = clock; }
|
|
16
|
+
async update(operation) {
|
|
17
|
+
await mkdir(dirname(this.path), { recursive: true, mode: 0o700 });
|
|
18
|
+
const release = await acquireWorkspaceLock(dirname(this.path));
|
|
19
|
+
try {
|
|
20
|
+
let state;
|
|
21
|
+
try { state = schema.parse(JSON.parse(await readFile(this.path, "utf8"))); }
|
|
22
|
+
catch (error) {
|
|
23
|
+
if (error.code !== "ENOENT") throw new Error("TaskChef cannot read notifications.json. Repair the file and refresh.");
|
|
24
|
+
state = empty();
|
|
25
|
+
}
|
|
26
|
+
const before = JSON.stringify({ ...state, observedAt: 0 });
|
|
27
|
+
const beforeObservedAt = state.observedAt;
|
|
28
|
+
await operation(state);
|
|
29
|
+
state.items = state.items.slice(0, 100);
|
|
30
|
+
const changed = JSON.stringify({ ...state, observedAt: 0 }) !== before;
|
|
31
|
+
if (changed) state.revision += 1;
|
|
32
|
+
if (changed || state.observedAt !== beforeObservedAt) {
|
|
33
|
+
await writeDurableAtomic(this.path, JSON.stringify(state));
|
|
34
|
+
}
|
|
35
|
+
return publicState(state);
|
|
36
|
+
} finally { await release(); }
|
|
37
|
+
}
|
|
38
|
+
add(state, { taskId = null, title, detail = "", kind, read = false }) {
|
|
39
|
+
state.items.unshift({ id: randomUUID(), taskId, title, detail, kind, read, timestamp: this.now() });
|
|
40
|
+
}
|
|
41
|
+
async reconcile(getSnapshot, settings) {
|
|
42
|
+
const observedAt = this.clock();
|
|
43
|
+
// Network work must not hold the shared lock needed by local user actions.
|
|
44
|
+
const snapshot = await getSnapshot();
|
|
45
|
+
const notifications = await this.update((state) => {
|
|
46
|
+
// A slower earlier request must not replace a newer process’s baseline.
|
|
47
|
+
if (snapshot.healthy === false || observedAt <= state.observedAt) return;
|
|
48
|
+
state.observedAt = observedAt;
|
|
49
|
+
const chats = {};
|
|
50
|
+
for (const task of snapshot.tasks) {
|
|
51
|
+
const turn = task.turnId ?? null;
|
|
52
|
+
const previous = state.chats[task.id];
|
|
53
|
+
const announced = previous?.turn === turn ? previous.announced : [];
|
|
54
|
+
const current = { turn, status: task.status ?? null, announced: [...announced] };
|
|
55
|
+
// Archiving changes the board label, but must still consume the saved terminal event.
|
|
56
|
+
const terminal = task.observed?.lastTurnEvent;
|
|
57
|
+
const event = (task.status === "needs_input" || terminal === "completed") && task.inputSource === "ordinary"
|
|
58
|
+
? "ready" : task.status === "interrupted" || terminal === "interrupted" || terminal === "failed" ? "interrupted" : null;
|
|
59
|
+
const eligible = !task.observed?.archive && !task.manualDone
|
|
60
|
+
&& (settings.showExec || task.observed?.source !== "exec")
|
|
61
|
+
&& (settings.showCli || task.observed?.source !== "cli");
|
|
62
|
+
// Consume the baseline and hidden events too; enabling a setting must not replay old turns.
|
|
63
|
+
if (event && turn && !current.announced.includes(event)) {
|
|
64
|
+
current.announced.push(event);
|
|
65
|
+
if (state.initialized && eligible && (event !== "ready" || task.status === "needs_input") && (!previous || previous.turn !== turn || previous.status !== task.status)) {
|
|
66
|
+
this.add(state, { taskId: task.id, kind: event, title: event === "ready" ? `${task.title} is ready for input or review` : `${task.title} was interrupted` });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
chats[task.id] = current;
|
|
70
|
+
}
|
|
71
|
+
state.chats = chats;
|
|
72
|
+
state.initialized = true;
|
|
73
|
+
});
|
|
74
|
+
return { snapshot, notifications };
|
|
75
|
+
}
|
|
76
|
+
async confirmation(task) {
|
|
77
|
+
return this.update((state) => this.add(state, { taskId: task.id, title: `${task.title} marked Done`, kind: "confirmation", read: true }));
|
|
78
|
+
}
|
|
79
|
+
async action({ action, id, task, operation, error }) {
|
|
80
|
+
return this.update((state) => {
|
|
81
|
+
if (action === "read") { const item = state.items.find((item) => item.id === id); if (item) item.read = true; }
|
|
82
|
+
else if (action === "read_all") state.items.forEach((item) => { item.read = true; });
|
|
83
|
+
else if (action === "clear") state.items = [];
|
|
84
|
+
else if (action === "error") {
|
|
85
|
+
const titles = { move: `Could not move ${task?.title ?? "chat"}`, open: `Could not open ${task?.title ?? "chat"}`, done: `Could not mark ${task?.title ?? "chat"} Done`, copy: "Could not copy chat ID", settings: "Could not open plugin settings" };
|
|
86
|
+
this.add(state, { taskId: task?.id ?? null, kind: "error", title: titles[operation], detail: error.slice(0, 1000) });
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { CodexSessionScanner } from "./codex-session-scanner.js";
|
|
2
|
+
import { acquireWorkspaceLock } from "./workspace.js";
|
|
3
|
+
import { readUsageStore, writeUsageStore } from "./usage.js";
|
|
4
|
+
import { TASKCHEF_VERSION } from "./version.js";
|
|
5
|
+
|
|
6
|
+
function projection(value, provenance, now, sessionCount) {
|
|
7
|
+
return {
|
|
8
|
+
inputTokens: value.tokens.input_tokens, cachedInputTokens: value.tokens.cached_input_tokens,
|
|
9
|
+
outputTokens: value.tokens.output_tokens, reasoningOutputTokens: value.tokens.reasoning_output_tokens,
|
|
10
|
+
totalTokens: value.tokens.total_tokens, partial: value.partial, costPartial: value.costPartial,
|
|
11
|
+
estimatedCostUsd: value.samples && (!value.costPartial || value.costUsd > 0) ? value.costUsd : null,
|
|
12
|
+
costStatus: value.samples && (!value.costPartial || value.costUsd > 0) ? "estimated" : "unavailable",
|
|
13
|
+
models: {}, provenance: { ...provenance, sessionCount }, sampledAt: now, sourceUpdatedAt: now,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function createRecordedUsageTracker({ workspace, maxConcurrentJobs = 2, scanner = new CodexSessionScanner() }) {
|
|
18
|
+
const pending = new Map(), queue = [];
|
|
19
|
+
let active = 0, closed = false;
|
|
20
|
+
function drain() {
|
|
21
|
+
while (!closed && active < maxConcurrentJobs && queue.length) {
|
|
22
|
+
const job = queue.shift(); active++;
|
|
23
|
+
job.run().then(job.resolve, job.reject).finally(() => { active--; drain(); });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
async function calculate(task) {
|
|
27
|
+
const ref = task.latestTurn?.turnRef;
|
|
28
|
+
const now = new Date().toISOString();
|
|
29
|
+
let result, failure;
|
|
30
|
+
try { result = await scanner.recordedUsage(task.threadId, task.latestTurn?.turnId ?? ref); }
|
|
31
|
+
catch { failure = "Saved Codex usage is unavailable. Usage requires Node.js 22.18+ and readable Codex records."; }
|
|
32
|
+
if (closed) return null;
|
|
33
|
+
const release = await acquireWorkspaceLock(workspace);
|
|
34
|
+
try {
|
|
35
|
+
const store = await readUsageStore(workspace);
|
|
36
|
+
const previous = store.tasks[task.id]?.threadId === task.threadId ? store.tasks[task.id] : null;
|
|
37
|
+
if (previous?.generationTurnCount > task.turns.length) return previous;
|
|
38
|
+
const provenance = { provider: "taskchef", version: TASKCHEF_VERSION, pricingMode: "standard" };
|
|
39
|
+
let total = result?.total.samples ? projection(result.total, provenance, now, result.subagents + 1)
|
|
40
|
+
: previous?.task ? { ...previous.task, partial: true, costPartial: true } : null;
|
|
41
|
+
// A partial scan cannot erase usage already known from older rollouts.
|
|
42
|
+
if (result?.total.partial && previous?.task && total.totalTokens < previous.task.totalTokens) {
|
|
43
|
+
total = { ...previous.task, partial: true, costPartial: true };
|
|
44
|
+
}
|
|
45
|
+
if (result?.total.costPartial && previous?.task?.estimatedCostUsd != null
|
|
46
|
+
&& (total.estimatedCostUsd == null || total.estimatedCostUsd < previous.task.estimatedCostUsd)) {
|
|
47
|
+
total = { ...total, estimatedCostUsd: previous.task.estimatedCostUsd, costStatus: "estimated", costPartial: true };
|
|
48
|
+
}
|
|
49
|
+
const priorTurn = previous?.turns[ref];
|
|
50
|
+
let latest = result?.matchedTurn && result.latest.samples
|
|
51
|
+
? { status: "available", ...projection(result.latest, provenance, now, result.subagents + 1), updatedAt: now }
|
|
52
|
+
: priorTurn?.status === "available" ? { ...priorTurn, partial: true, costPartial: true, updatedAt: now }
|
|
53
|
+
: { status: "unavailable", reason: failure || (result?.matchedTurn ? "No saved usage for this native turn." : "This dispatcher turn has no matching native Codex turn ID."), updatedAt: now };
|
|
54
|
+
if (latest.status === "available" && latest.partial && priorTurn?.status === "available" && latest.totalTokens < priorTurn.totalTokens) {
|
|
55
|
+
latest = { ...priorTurn, partial: true, costPartial: true, updatedAt: now };
|
|
56
|
+
}
|
|
57
|
+
if (latest.status === "available" && latest.costPartial && priorTurn?.estimatedCostUsd != null
|
|
58
|
+
&& (latest.estimatedCostUsd == null || latest.estimatedCostUsd < priorTurn.estimatedCostUsd)) {
|
|
59
|
+
latest = { ...latest, estimatedCostUsd: priorTurn.estimatedCostUsd, costStatus: "estimated", costPartial: true };
|
|
60
|
+
}
|
|
61
|
+
const record = {
|
|
62
|
+
threadId: task.threadId, generationTurnRef: ref, generationTurnCount: task.turns.length,
|
|
63
|
+
generationTerminal: !!task.latestTurn?.result, zeroBaselineTurnRef: null,
|
|
64
|
+
status: total ? "available" : "unavailable", updatedAt: now, retryAfter: null,
|
|
65
|
+
...(total ? {} : { reason: failure || "No saved usage records were found." }),
|
|
66
|
+
task: total, turns: { ...(previous?.turns ?? {}), [ref]: latest }, boundaries: {},
|
|
67
|
+
};
|
|
68
|
+
store.tasks[task.id] = record;
|
|
69
|
+
await writeUsageStore(workspace, store);
|
|
70
|
+
return record;
|
|
71
|
+
} finally { await release(); }
|
|
72
|
+
}
|
|
73
|
+
function get(task) {
|
|
74
|
+
if (closed) return Promise.resolve(null);
|
|
75
|
+
if (!task.threadId || !task.latestTurn) return Promise.resolve({ status: "unavailable", reason: "No linked Codex turn.", task: null, turns: {} });
|
|
76
|
+
if (task.latestTurn.provenance?.kind === "dashboard_manual") {
|
|
77
|
+
return readUsageStore(workspace).then(store => ({ ...(store.tasks[task.id] ?? { status: "unavailable", task: null }), turns: { ...(store.tasks[task.id]?.turns ?? {}), [task.latestTurn.turnRef]: { status: "unavailable", reason: "Administrative action; no Codex usage boundary." } } }));
|
|
78
|
+
}
|
|
79
|
+
const key = `${task.id}:${task.latestTurn.turnRef}`;
|
|
80
|
+
if (pending.has(key)) return pending.get(key);
|
|
81
|
+
const promise = new Promise((resolve, reject) => { queue.push({ run: () => calculate(task), resolve, reject }); drain(); });
|
|
82
|
+
pending.set(key, promise);
|
|
83
|
+
promise.finally(() => pending.delete(key)).catch(() => {});
|
|
84
|
+
return promise;
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
get, observe: get,
|
|
88
|
+
// Historical tasks stay lazy; opening Details reads their saved logs.
|
|
89
|
+
preload: async () => {},
|
|
90
|
+
close() { closed = true; for (const job of queue.splice(0)) job.resolve(null); scanner.close(); },
|
|
91
|
+
};
|
|
92
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { fromMarkdown } from 'mdast-util-from-markdown';
|
|
2
|
+
import { isAbsolute } from 'node:path';
|
|
3
|
+
import { open } from 'node:fs/promises';
|
|
4
|
+
import { constants } from 'node:fs';
|
|
5
|
+
|
|
6
|
+
// Parse actual Markdown image nodes, excluding code examples and raw HTML.
|
|
7
|
+
// Multipart queued prompts can define image references in another text part.
|
|
8
|
+
export function replyImages(text, referenceText = text) {
|
|
9
|
+
if (referenceText !== text) {
|
|
10
|
+
const definitions = [];
|
|
11
|
+
const collectDefinitions = node => {
|
|
12
|
+
if (node.type === 'definition') definitions.push(referenceText.slice(node.position.start.offset, node.position.end.offset));
|
|
13
|
+
for (const child of node.children ?? []) collectDefinitions(child);
|
|
14
|
+
};
|
|
15
|
+
collectDefinitions(fromMarkdown(referenceText));
|
|
16
|
+
text += `\n\n${definitions.join('\n')}`;
|
|
17
|
+
}
|
|
18
|
+
const tree = fromMarkdown(text);
|
|
19
|
+
const definitions = new Map();
|
|
20
|
+
const collect = (node) => { if (node.type === 'definition') definitions.set(node.identifier, node.url); for (const child of node.children ?? []) collect(child); };
|
|
21
|
+
collect(tree);
|
|
22
|
+
const images = [];
|
|
23
|
+
const visit = (node) => {
|
|
24
|
+
const url = node.type === 'image' ? node.url : node.type === 'imageReference' ? definitions.get(node.identifier) : null;
|
|
25
|
+
if (typeof url === 'string' && isAbsolute(url) && !url.startsWith('//')) images.push({ url, alt: node.alt || 'Reply image' });
|
|
26
|
+
for (const child of node.children ?? []) visit(child);
|
|
27
|
+
};
|
|
28
|
+
visit(tree);
|
|
29
|
+
return images;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function replyImage(text) { return replyImages(text)[0] ?? null; }
|
|
33
|
+
|
|
34
|
+
// Read only the image referenced by the selected reply. Never accept a path
|
|
35
|
+
// supplied independently by the UI, fetch URLs, or serve active SVG content.
|
|
36
|
+
export async function localReplyImage(path, { maxBytes = 4 * 1024 * 1024 } = {}) {
|
|
37
|
+
if (!isAbsolute(path) || path.startsWith('//')) return null;
|
|
38
|
+
let file;
|
|
39
|
+
try { file = await open(path, constants.O_RDONLY | constants.O_NONBLOCK); } catch (error) {
|
|
40
|
+
// Keep literal filenames working, then resolve Markdown URI escapes.
|
|
41
|
+
if (error.code !== "ENOENT") throw error;
|
|
42
|
+
const decoded = decodeURIComponent(path);
|
|
43
|
+
if (decoded === path || !isAbsolute(decoded) || decoded.startsWith("//")) throw error;
|
|
44
|
+
file = await open(decoded, constants.O_RDONLY | constants.O_NONBLOCK);
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
const info = await file.stat();
|
|
48
|
+
const limit = maxBytes;
|
|
49
|
+
if (!info.isFile() || info.size > limit) return null;
|
|
50
|
+
const bytes = Buffer.alloc(limit + 1);
|
|
51
|
+
let length = 0;
|
|
52
|
+
while (length < bytes.length) {
|
|
53
|
+
const result = await file.read(bytes, length, bytes.length - length, null);
|
|
54
|
+
if (!result.bytesRead) break;
|
|
55
|
+
length += result.bytesRead;
|
|
56
|
+
}
|
|
57
|
+
if (length > limit) return null;
|
|
58
|
+
const data = bytes.subarray(0, length);
|
|
59
|
+
const mime = data.subarray(0, 8).equals(Buffer.from([137,80,78,71,13,10,26,10])) ? 'image/png'
|
|
60
|
+
: data[0] === 255 && data[1] === 216 && data[2] === 255 ? 'image/jpeg'
|
|
61
|
+
: /^GIF8[79]a$/.test(data.subarray(0,6).toString()) ? 'image/gif'
|
|
62
|
+
: data.subarray(0,4).toString() === 'RIFF' && data.subarray(8,12).toString() === 'WEBP' ? 'image/webp' : null;
|
|
63
|
+
return mime ? `data:${mime};base64,${data.toString('base64')}` : null;
|
|
64
|
+
} finally { await file.close(); }
|
|
65
|
+
}
|
package/src/usage-tracker.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { createRecordedUsageTracker } from "./recorded-usage-tracker.js";
|
|
1
2
|
import { realpath } from "node:fs/promises";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
|
|
4
5
|
import { acquireWorkspaceLock } from "./workspace.js";
|
|
5
6
|
import {
|
|
6
|
-
readCcusageThreadUsage,
|
|
7
7
|
readUsageStore,
|
|
8
8
|
usageDelta,
|
|
9
9
|
writeUsageStore,
|
|
@@ -47,10 +47,10 @@ function generationIsOlder(task, existing) {
|
|
|
47
47
|
|
|
48
48
|
function publicReason(error) {
|
|
49
49
|
const message = String(error?.message ?? "");
|
|
50
|
-
if (/not installed/i.test(message)) return "
|
|
50
|
+
if (/not installed/i.test(message)) return "Usage provider is not installed.";
|
|
51
51
|
if (/could not resolve/i.test(message)) return "No matching Codex usage session was found.";
|
|
52
|
-
if (/timed out/i.test(message)) return "
|
|
53
|
-
return "Codex usage is unavailable
|
|
52
|
+
if (/timed out/i.test(message)) return "Usage calculation did not finish in time.";
|
|
53
|
+
return "Codex usage is unavailable.";
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
function calculatingTask(task, existing = null, now = new Date().toISOString()) {
|
|
@@ -202,7 +202,7 @@ function snapshotCorrects(current, incoming) {
|
|
|
202
202
|
function correctedRecord(task, snapshot) {
|
|
203
203
|
const now = snapshot.sampledAt;
|
|
204
204
|
const version = snapshot.provenance.version ? ` ${snapshot.provenance.version}` : "";
|
|
205
|
-
const reason = `
|
|
205
|
+
const reason = `The usage provider${version} corrected an older cumulative total; reliable per-turn usage is unavailable.`;
|
|
206
206
|
const turns = Object.fromEntries(task.turns.slice(-MAX_TRACKED_TURNS).map((turn) => [
|
|
207
207
|
turn.turnRef,
|
|
208
208
|
isAdministrativeTurn(turn)
|
|
@@ -328,7 +328,7 @@ function reconcileRecord(task, existing, snapshot, { boundaryReliable = true } =
|
|
|
328
328
|
export function createUsageTracker({
|
|
329
329
|
workspace,
|
|
330
330
|
maxConcurrentJobs = 2,
|
|
331
|
-
readThreadUsage
|
|
331
|
+
readThreadUsage,
|
|
332
332
|
retryDelaysMs = [2_000, 3_000, 4_000, 1_000],
|
|
333
333
|
retryCooldownMs = 60_000,
|
|
334
334
|
setTimer = setTimeout,
|
|
@@ -336,6 +336,7 @@ export function createUsageTracker({
|
|
|
336
336
|
if (!Number.isSafeInteger(maxConcurrentJobs) || maxConcurrentJobs < 1 || maxConcurrentJobs > 8) {
|
|
337
337
|
throw new Error("usage tracker concurrency must be an integer from 1 to 8");
|
|
338
338
|
}
|
|
339
|
+
if (!readThreadUsage) return createRecordedUsageTracker({ workspace, maxConcurrentJobs });
|
|
339
340
|
const jobs = new Map();
|
|
340
341
|
const observationChains = new Map();
|
|
341
342
|
const readyJobs = [];
|