dsh-super-code 0.3.11 → 0.4.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/LICENSE +1 -1
- package/README.en.md +50 -18
- package/README.md +50 -18
- package/builtin/README.md +22 -0
- package/builtin/skills/mattpocock/LICENSE +21 -0
- package/builtin/skills/mattpocock/UPSTREAM.md +11 -0
- package/builtin/skills/mattpocock/distribution.json +352 -0
- package/builtin/skills/mattpocock/skills/ask-matt/PHASE-BOUNDARIES.md +55 -0
- package/builtin/skills/mattpocock/skills/ask-matt/SKILL.md +97 -0
- package/builtin/skills/mattpocock/skills/ask-matt/agents/openai.yaml +5 -0
- package/builtin/skills/mattpocock/skills/code-review/SKILL.md +89 -0
- package/builtin/skills/mattpocock/skills/code-review/agents/openai.yaml +3 -0
- package/builtin/skills/mattpocock/skills/codebase-design/DEEPENING.md +37 -0
- package/builtin/skills/mattpocock/skills/codebase-design/DESIGN-IT-TWICE.md +44 -0
- package/builtin/skills/mattpocock/skills/codebase-design/SKILL.md +114 -0
- package/builtin/skills/mattpocock/skills/codebase-design/agents/openai.yaml +3 -0
- package/builtin/skills/mattpocock/skills/diagnosing-bugs/SKILL.md +138 -0
- package/builtin/skills/mattpocock/skills/diagnosing-bugs/agents/openai.yaml +3 -0
- package/builtin/skills/mattpocock/skills/diagnosing-bugs/scripts/hitl-loop.template.sh +44 -0
- package/builtin/skills/mattpocock/skills/domain-modeling/ADR-FORMAT.md +47 -0
- package/builtin/skills/mattpocock/skills/domain-modeling/GLOSSARY-FORMAT.md +60 -0
- package/builtin/skills/mattpocock/skills/domain-modeling/SKILL.md +74 -0
- package/builtin/skills/mattpocock/skills/domain-modeling/agents/openai.yaml +3 -0
- package/builtin/skills/mattpocock/skills/grill-me/SKILL.md +7 -0
- package/builtin/skills/mattpocock/skills/grill-me/agents/openai.yaml +5 -0
- package/builtin/skills/mattpocock/skills/grill-with-docs/SKILL.md +7 -0
- package/builtin/skills/mattpocock/skills/grill-with-docs/agents/openai.yaml +5 -0
- package/builtin/skills/mattpocock/skills/grilling/SKILL.md +30 -0
- package/builtin/skills/mattpocock/skills/grilling/agents/openai.yaml +3 -0
- package/builtin/skills/mattpocock/skills/handoff/SKILL.md +16 -0
- package/builtin/skills/mattpocock/skills/handoff/agents/openai.yaml +5 -0
- package/builtin/skills/mattpocock/skills/implement/SKILL.md +17 -0
- package/builtin/skills/mattpocock/skills/implement/agents/openai.yaml +5 -0
- package/builtin/skills/mattpocock/skills/implement-spec/SKILL.md +42 -0
- package/builtin/skills/mattpocock/skills/implement-spec/agents/openai.yaml +5 -0
- package/builtin/skills/mattpocock/skills/improve-codebase-architecture/HTML-REPORT.md +123 -0
- package/builtin/skills/mattpocock/skills/improve-codebase-architecture/SKILL.md +71 -0
- package/builtin/skills/mattpocock/skills/improve-codebase-architecture/agents/openai.yaml +5 -0
- package/builtin/skills/mattpocock/skills/pr/CREDITS.md +3 -0
- package/builtin/skills/mattpocock/skills/pr/SKILL.md +170 -0
- package/builtin/skills/mattpocock/skills/pr/agents/openai.yaml +3 -0
- package/builtin/skills/mattpocock/skills/prototype/LOGIC.md +67 -0
- package/builtin/skills/mattpocock/skills/prototype/SKILL.md +26 -0
- package/builtin/skills/mattpocock/skills/prototype/UI.md +112 -0
- package/builtin/skills/mattpocock/skills/prototype/agents/openai.yaml +3 -0
- package/builtin/skills/mattpocock/skills/research/SKILL.md +12 -0
- package/builtin/skills/mattpocock/skills/research/agents/openai.yaml +3 -0
- package/builtin/skills/mattpocock/skills/retro/SKILL.md +44 -0
- package/builtin/skills/mattpocock/skills/retro/agents/openai.yaml +5 -0
- package/builtin/skills/mattpocock/skills/setup-matt-pocock-skills/SKILL.md +120 -0
- package/builtin/skills/mattpocock/skills/setup-matt-pocock-skills/agents/openai.yaml +5 -0
- package/builtin/skills/mattpocock/skills/setup-matt-pocock-skills/domain.md +51 -0
- package/builtin/skills/mattpocock/skills/setup-matt-pocock-skills/issue-tracker-github.md +50 -0
- package/builtin/skills/mattpocock/skills/setup-matt-pocock-skills/issue-tracker-gitlab.md +46 -0
- package/builtin/skills/mattpocock/skills/setup-matt-pocock-skills/issue-tracker-local.md +30 -0
- package/builtin/skills/mattpocock/skills/setup-matt-pocock-skills/triage-labels.md +15 -0
- package/builtin/skills/mattpocock/skills/tdd/SKILL.md +38 -0
- package/builtin/skills/mattpocock/skills/tdd/agents/openai.yaml +3 -0
- package/builtin/skills/mattpocock/skills/tdd/mocking.md +59 -0
- package/builtin/skills/mattpocock/skills/tdd/tests.md +77 -0
- package/builtin/skills/mattpocock/skills/teach/GLOSSARY-FORMAT.md +35 -0
- package/builtin/skills/mattpocock/skills/teach/LEARNING-RECORD-FORMAT.md +46 -0
- package/builtin/skills/mattpocock/skills/teach/MISSION-FORMAT.md +31 -0
- package/builtin/skills/mattpocock/skills/teach/RESOURCES-FORMAT.md +32 -0
- package/builtin/skills/mattpocock/skills/teach/SKILL.md +140 -0
- package/builtin/skills/mattpocock/skills/teach/agents/openai.yaml +5 -0
- package/builtin/skills/mattpocock/skills/to-questionnaire/SKILL.md +54 -0
- package/builtin/skills/mattpocock/skills/to-questionnaire/agents/openai.yaml +5 -0
- package/builtin/skills/mattpocock/skills/to-spec/SKILL.md +75 -0
- package/builtin/skills/mattpocock/skills/to-spec/agents/openai.yaml +5 -0
- package/builtin/skills/mattpocock/skills/to-tickets/SKILL.md +105 -0
- package/builtin/skills/mattpocock/skills/to-tickets/agents/openai.yaml +5 -0
- package/builtin/skills/mattpocock/skills/triage/AGENT-BRIEF.md +207 -0
- package/builtin/skills/mattpocock/skills/triage/OUT-OF-SCOPE.md +105 -0
- package/builtin/skills/mattpocock/skills/triage/SKILL.md +112 -0
- package/builtin/skills/mattpocock/skills/triage/agents/openai.yaml +5 -0
- package/builtin/skills/mattpocock/skills/wait-what/SKILL.md +7 -0
- package/builtin/skills/mattpocock/skills/wait-what/agents/openai.yaml +5 -0
- package/builtin/skills/mattpocock/skills/wayfinder/SKILL.md +128 -0
- package/builtin/skills/mattpocock/skills/wayfinder/agents/openai.yaml +5 -0
- package/builtin/skills/mattpocock/skills/wizard/SKILL.md +44 -0
- package/builtin/skills/mattpocock/skills/wizard/agents/openai.yaml +3 -0
- package/builtin/skills/mattpocock/skills/wizard/template.sh +223 -0
- package/builtin/skills/mattpocock/skills/writing-for-agents/SKILL-MECHANICS.md +22 -0
- package/builtin/skills/mattpocock/skills/writing-for-agents/SKILL.md +81 -0
- package/builtin/skills/mattpocock/skills/writing-for-agents/agents/openai.yaml +3 -0
- package/builtin/skills/oil-ui/LICENSE +21 -0
- package/builtin/skills/oil-ui/SKILL.md +163 -0
- package/builtin/skills/oil-ui/UPSTREAM.md +14 -0
- package/builtin/skills/oil-ui/assets/fonts/instrument-serif/InstrumentSerif-Regular.ttf +0 -0
- package/builtin/skills/oil-ui/assets/fonts/instrument-serif/OFL.txt +93 -0
- package/builtin/skills/oil-ui/assets/logo.png +0 -0
- package/builtin/skills/oil-ui/assets/style-explorer.html +433 -0
- package/builtin/skills/oil-ui/distribution.json +5 -0
- package/builtin/skills/oil-ui/references/design-direction.md +233 -0
- package/builtin/skills/oil-ui/references/direction-persuasion.md +25 -0
- package/builtin/skills/oil-ui/references/image-ideation.md +130 -0
- package/builtin/skills/oil-ui/references/layout-and-viewport.md +24 -0
- package/builtin/skills/oil-ui/references/media.md +158 -0
- package/builtin/skills/oil-ui/references/motion.md +123 -0
- package/builtin/skills/oil-ui/references/play.md +29 -0
- package/builtin/skills/oil-ui/references/reviewer-manual.md +73 -0
- package/builtin/skills/oil-ui/references/scroll-narrative.md +97 -0
- package/builtin/skills/oil-ui/references/style-explorer.md +48 -0
- package/builtin/skills/oil-ui/references/task-explore.md +28 -0
- package/builtin/skills/oil-ui/references/task-review.md +8 -0
- package/builtin/skills/oil-ui/references/tools.md +75 -0
- package/builtin/skills/oil-ui/references/ux-review.md +23 -0
- package/builtin/skills/oil-ui/references/ux-reviewer.md +10 -0
- package/builtin/skills/oil-ui/references/visual-language.md +103 -0
- package/builtin/skills/oil-ui/references/visual-review.md +35 -0
- package/builtin/skills/oil-ui/references/workdir.md +64 -0
- package/builtin/skills/oil-ui/scripts/build_explorer.py +958 -0
- package/builtin/skills/oil-ui/scripts/chrome.mjs +115 -0
- package/builtin/skills/oil-ui/scripts/curate-media.mjs +726 -0
- package/builtin/skills/oil-ui/scripts/measure_reference.py +543 -0
- package/builtin/skills/oil-ui/scripts/oiloil-image.mjs +388 -0
- package/builtin/skills/oil-ui/scripts/prep-image.mjs +287 -0
- package/builtin/skills/oil-ui/scripts/recommend_once.py +65 -0
- package/builtin/skills/oil-ui/scripts/shoot.mjs +2067 -0
- package/builtin/skills/openspec/LICENSE +22 -0
- package/builtin/skills/openspec/UPSTREAM.md +11 -0
- package/builtin/skills/openspec/distribution.json +73 -0
- package/builtin/skills/openspec/skills/README.md +19 -0
- package/builtin/skills/openspec/skills/openspec-apply-change/SKILL.md +205 -0
- package/builtin/skills/openspec/skills/openspec-archive-change/SKILL.md +220 -0
- package/builtin/skills/openspec/skills/openspec-bulk-archive-change/SKILL.md +376 -0
- package/builtin/skills/openspec/skills/openspec-continue-change/SKILL.md +137 -0
- package/builtin/skills/openspec/skills/openspec-explore/SKILL.md +355 -0
- package/builtin/skills/openspec/skills/openspec-ff-change/SKILL.md +128 -0
- package/builtin/skills/openspec/skills/openspec-new-change/SKILL.md +87 -0
- package/builtin/skills/openspec/skills/openspec-onboard/SKILL.md +574 -0
- package/builtin/skills/openspec/skills/openspec-propose/SKILL.md +174 -0
- package/builtin/skills/openspec/skills/openspec-sync-specs/SKILL.md +289 -0
- package/builtin/skills/openspec/skills/openspec-update-change/SKILL.md +108 -0
- package/builtin/skills/openspec/skills/openspec-verify-change/SKILL.md +222 -0
- package/builtin/skills/pstack/LICENSE +21 -0
- package/builtin/skills/pstack/UPSTREAM.md +17 -0
- package/builtin/skills/pstack/distribution.json +2299 -0
- package/builtin/skills/pstack/dsh/DSH.md +59 -0
- package/builtin/skills/pstack/dsh/LICENSE +21 -0
- package/builtin/skills/pstack/dsh/README.md +9 -0
- package/builtin/skills/pstack/dsh/adaptation.json +2930 -0
- package/builtin/skills/pstack/dsh/agents/comment-sicko.md +32 -0
- package/builtin/skills/pstack/dsh/agents/poteto-agent.md +5 -0
- package/builtin/skills/pstack/dsh/assets/logo.png +0 -0
- package/builtin/skills/pstack/dsh/authoring.md +7 -0
- package/builtin/skills/pstack/dsh/docs/guide/01-setup.md +68 -0
- package/builtin/skills/pstack/dsh/docs/guide/02-poteto-mode.md +141 -0
- package/builtin/skills/pstack/dsh/docs/guide/03-understand.md +81 -0
- package/builtin/skills/pstack/dsh/docs/guide/04-design.md +135 -0
- package/builtin/skills/pstack/dsh/docs/guide/05-build-and-clean.md +85 -0
- package/builtin/skills/pstack/dsh/docs/guide/06-verify-and-ship.md +164 -0
- package/builtin/skills/pstack/dsh/docs/guide/07-overnight.md +124 -0
- package/builtin/skills/pstack/dsh/docs/guide/08-principles.md +74 -0
- package/builtin/skills/pstack/dsh/docs/guide/09-make-it-yours.md +102 -0
- package/builtin/skills/pstack/dsh/docs/guide/10-recipes-and-pitfalls.md +184 -0
- package/builtin/skills/pstack/dsh/docs/guide/README.md +40 -0
- package/builtin/skills/pstack/dsh/docs/guide/images/design.jpg +0 -0
- package/builtin/skills/pstack/dsh/docs/guide/images/overnight.jpg +0 -0
- package/builtin/skills/pstack/dsh/docs/guide/images/recipes.jpg +0 -0
- package/builtin/skills/pstack/dsh/docs/guide/images/router.jpg +0 -0
- package/builtin/skills/pstack/dsh/docs/guide/images/understanding.jpg +0 -0
- package/builtin/skills/pstack/dsh/docs/guide/images/verification.jpg +0 -0
- package/builtin/skills/pstack/dsh/docs/harness/claude-code.md +69 -0
- package/builtin/skills/pstack/dsh/scripts/capture.mjs +80 -0
- package/builtin/skills/pstack/dsh/skills/architect/SKILL.md +82 -0
- package/builtin/skills/pstack/dsh/skills/architect/references/design-red-flags.md +57 -0
- package/builtin/skills/pstack/dsh/skills/architect/references/rationale-template.md +35 -0
- package/builtin/skills/pstack/dsh/skills/architect/references/runner-prompt.md +20 -0
- package/builtin/skills/pstack/dsh/skills/arena/SKILL.md +51 -0
- package/builtin/skills/pstack/dsh/skills/automate-me/SKILL.md +19 -0
- package/builtin/skills/pstack/dsh/skills/benchmark-checklist/SKILL.md +42 -0
- package/builtin/skills/pstack/dsh/skills/blast-radius/SKILL.md +53 -0
- package/builtin/skills/pstack/dsh/skills/bro/SKILL.md +10 -0
- package/builtin/skills/pstack/dsh/skills/correct/SKILL.md +35 -0
- package/builtin/skills/pstack/dsh/skills/create-formal-verification-skill/SKILL.md +46 -0
- package/builtin/skills/pstack/dsh/skills/create-verification-skill/SKILL.md +47 -0
- package/builtin/skills/pstack/dsh/skills/create-verification-skill/references/feature-map-example/README.md +47 -0
- package/builtin/skills/pstack/dsh/skills/create-verification-skill/references/feature-map-example/create-note.md +39 -0
- package/builtin/skills/pstack/dsh/skills/create-verification-skill/references/feature-map-example/search.md +45 -0
- package/builtin/skills/pstack/dsh/skills/figure-it-out/SKILL.md +55 -0
- package/builtin/skills/pstack/dsh/skills/how/SKILL.md +39 -0
- package/builtin/skills/pstack/dsh/skills/how/references/explainer-prompt.md +55 -0
- package/builtin/skills/pstack/dsh/skills/how/references/explorer-prompt.md +35 -0
- package/builtin/skills/pstack/dsh/skills/in-your-own-words/SKILL.md +10 -0
- package/builtin/skills/pstack/dsh/skills/interrogate/SKILL.md +34 -0
- package/builtin/skills/pstack/dsh/skills/ketchup/SKILL.md +46 -0
- package/builtin/skills/pstack/dsh/skills/ketchup-video/SKILL.md +32 -0
- package/builtin/skills/pstack/dsh/skills/maintain-formal-verification-skill/SKILL.md +61 -0
- package/builtin/skills/pstack/dsh/skills/maintain-verification-skill/SKILL.md +42 -0
- package/builtin/skills/pstack/dsh/skills/make-bot-ui/SKILL.md +22 -0
- package/builtin/skills/pstack/dsh/skills/make-bot-ui/references/submissions.md +44 -0
- package/builtin/skills/pstack/dsh/skills/make-bot-ui/scripts/submissions.mjs +86 -0
- package/builtin/skills/pstack/dsh/skills/no-comments/SKILL.md +27 -0
- package/builtin/skills/pstack/dsh/skills/poteto-help/SKILL.md +42 -0
- package/builtin/skills/pstack/dsh/skills/poteto-help/references/prompting.md +51 -0
- package/builtin/skills/pstack/dsh/skills/poteto-help/references/recipes.md +51 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/SKILL.md +153 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/playbooks/authoring-a-skill.md +8 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/playbooks/autonomous-run.md +12 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/playbooks/autopilot-full.md +25 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/playbooks/autopilot-stack.md +8 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/playbooks/babysit.md +23 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/playbooks/bug-fix.md +15 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/playbooks/eval.md +25 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/playbooks/feature.md +21 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/playbooks/hillclimb.md +21 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/playbooks/investigation.md +14 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/playbooks/multi-phase-plan.md +155 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/playbooks/opening-a-pr.md +34 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/playbooks/orchestrate.md +14 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/playbooks/pause-safely.md +8 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/playbooks/perf-issue.md +25 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/playbooks/prototype.md +14 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/playbooks/refactoring.md +16 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/playbooks/runtime-forensics.md +11 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/playbooks/session-pickup.md +9 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/playbooks/shipping.md +12 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/playbooks/trace-forensics.md +14 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/playbooks/visual-parity.md +11 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/playbooks/worktree-cleanup.md +9 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/references/bugbot-triage.md +142 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/scripts/bootstrap.ts +62 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/scripts/bun.lock +67 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/scripts/check-plan.mjs +205 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/scripts/check-plan.test.ts +110 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/scripts/package.json +16 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/scripts/watch-pr/cli.test.ts +224 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/scripts/watch-pr/cli.ts +223 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/scripts/watch-pr/fakes.test-helper.ts +129 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/scripts/watch-pr/github.test.ts +498 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/scripts/watch-pr/github.ts +726 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/scripts/watch-pr/policy.test.ts +482 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/scripts/watch-pr/policy.ts +842 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/scripts/watch-pr/render.ts +169 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/scripts/watch-pr/tsconfig.json +13 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/scripts/watch-pr/types.compile.ts +93 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/scripts/watch-pr/types.ts +414 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/scripts/watch-pr/watch-pr +6 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/scripts/worktree-audit.mjs +52 -0
- package/builtin/skills/pstack/dsh/skills/poteto-mode/scripts/worktree-audit.sh +4 -0
- package/builtin/skills/pstack/dsh/skills/principle-attack-the-premise/SKILL.md +24 -0
- package/builtin/skills/pstack/dsh/skills/principle-boundary-discipline/SKILL.md +37 -0
- package/builtin/skills/pstack/dsh/skills/principle-build-the-lever/SKILL.md +26 -0
- package/builtin/skills/pstack/dsh/skills/principle-encode-lessons-in-structure/SKILL.md +14 -0
- package/builtin/skills/pstack/dsh/skills/principle-exhaust-the-design-space/SKILL.md +12 -0
- package/builtin/skills/pstack/dsh/skills/principle-experience-first/SKILL.md +22 -0
- package/builtin/skills/pstack/dsh/skills/principle-explain-the-number/SKILL.md +21 -0
- package/builtin/skills/pstack/dsh/skills/principle-fix-root-causes/SKILL.md +24 -0
- package/builtin/skills/pstack/dsh/skills/principle-foundational-thinking/SKILL.md +24 -0
- package/builtin/skills/pstack/dsh/skills/principle-guard-the-context-window/SKILL.md +15 -0
- package/builtin/skills/pstack/dsh/skills/principle-laziness-protocol/SKILL.md +13 -0
- package/builtin/skills/pstack/dsh/skills/principle-make-operations-idempotent/SKILL.md +25 -0
- package/builtin/skills/pstack/dsh/skills/principle-migrate-callers-then-delete-legacy-apis/SKILL.md +17 -0
- package/builtin/skills/pstack/dsh/skills/principle-minimize-reader-load/SKILL.md +21 -0
- package/builtin/skills/pstack/dsh/skills/principle-model-the-domain/SKILL.md +27 -0
- package/builtin/skills/pstack/dsh/skills/principle-never-block-on-the-human/SKILL.md +18 -0
- package/builtin/skills/pstack/dsh/skills/principle-outcome-oriented-execution/SKILL.md +24 -0
- package/builtin/skills/pstack/dsh/skills/principle-prove-it-works/SKILL.md +25 -0
- package/builtin/skills/pstack/dsh/skills/principle-redesign-from-first-principles/SKILL.md +19 -0
- package/builtin/skills/pstack/dsh/skills/principle-separate-before-serializing-shared-state/SKILL.md +19 -0
- package/builtin/skills/pstack/dsh/skills/principle-sequence-verifiable-units/SKILL.md +20 -0
- package/builtin/skills/pstack/dsh/skills/principle-subtract-before-you-add/SKILL.md +21 -0
- package/builtin/skills/pstack/dsh/skills/principle-test-behavior-not-implementation/SKILL.md +23 -0
- package/builtin/skills/pstack/dsh/skills/principle-type-system-discipline/SKILL.md +16 -0
- package/builtin/skills/pstack/dsh/skills/recall/SKILL.md +19 -0
- package/builtin/skills/pstack/dsh/skills/reflect/SKILL.md +19 -0
- package/builtin/skills/pstack/dsh/skills/reflect/references/divergent-reviewer.md +38 -0
- package/builtin/skills/pstack/dsh/skills/reflect/references/judgment-reviewer.md +37 -0
- package/builtin/skills/pstack/dsh/skills/reflect/references/synthesizer.md +56 -0
- package/builtin/skills/pstack/dsh/skills/reflect/references/tooling-reviewer.md +48 -0
- package/builtin/skills/pstack/dsh/skills/setup-pstack/SKILL.md +80 -0
- package/builtin/skills/pstack/dsh/skills/sharpen/SKILL.md +19 -0
- package/builtin/skills/pstack/dsh/skills/show-me-your-work/SKILL.md +85 -0
- package/builtin/skills/pstack/dsh/skills/show-me-your-work/references/decision-log-template.tsv +1 -0
- package/builtin/skills/pstack/dsh/skills/show-me-your-work/scripts/audit-log.mjs +43 -0
- package/builtin/skills/pstack/dsh/skills/show-me-your-work/scripts/log.sh +42 -0
- package/builtin/skills/pstack/dsh/skills/statechart/SKILL.md +85 -0
- package/builtin/skills/pstack/dsh/skills/statechart/references/render.md +93 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/backends/cfg.mjs +31 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/backends/cfg.ts +33 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/backends/mermaid.mjs +223 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/backends/mermaid.ts +201 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/backends/plain-text.mjs +68 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/backends/plain-text.ts +72 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/backends/report.mjs +123 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/backends/report.ts +177 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/frontends/config.mjs +258 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/frontends/config.ts +276 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/frontends/dot.mjs +68 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/frontends/dot.ts +74 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/frontends/marks.mjs +102 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/frontends/marks.ts +93 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/frontends/messages.mjs +53 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/frontends/messages.ts +52 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/frontends/spec.mjs +89 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/frontends/spec.ts +114 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/frontends/trace.mjs +24 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/frontends/trace.ts +24 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/frontends/value.mjs +181 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/frontends/value.ts +166 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/frontends/verdict.mjs +38 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/frontends/verdict.ts +40 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/frontends/view.mjs +107 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/frontends/view.ts +119 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/ir.mjs +317 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/ir.ts +547 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/package.json +11 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/passes/annotate.mjs +102 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/passes/annotate.ts +128 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/passes/label.mjs +217 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/passes/label.ts +269 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/passes/lift.mjs +89 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/passes/lift.ts +85 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/passes/loop-check.mjs +57 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/passes/loop-check.ts +68 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/passes/paths.mjs +50 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/passes/paths.ts +60 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/passes/reach.mjs +134 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/passes/reach.ts +135 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/passes/region-check.mjs +94 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/passes/region-check.ts +97 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/passes/regions.mjs +296 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/passes/regions.ts +295 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/pipeline.mjs +63 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/pipeline.ts +76 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/run.mjs +432 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/run.ts +481 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/statechart +29 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/statechart.mjs +149 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/statechart.ts +164 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/ConfigOracle.java +63 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/chart.test.ts +1210 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/charts/Braces.mmd +7 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/charts/Escapes.mmd +11 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/charts/Phases.mmd +29 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/cli.test.ts +1314 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/config-fuzz.test.ts +263 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/Counter/ChartRun.out +57 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/Counter/Counter.cfg +3 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/Counter/Counter.tla +10 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/Counter/chart.mmd +9 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/Counter/graph.dot +16 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/Counter/report.md +7 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/Counter/sany.out +2 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/Counter/verdict-1.out +49 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterAction/ChartRun.out +60 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterAction/CounterAction.cfg +4 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterAction/CounterAction.tla +22 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterAction/chart.mmd +26 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterAction/graph.dot +19 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterAction/report.md +20 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterAction/sany.out +2 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterAction/verdict-1.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterAction/verdict-1.out +56 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterAction/verdict-2.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterAction/verdict-2.out +82 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterAction/verdict-3.out +57 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterDeadlock/ChartRun.out +59 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterDeadlock/CounterDeadlock.cfg +3 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterDeadlock/CounterDeadlock.tla +11 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterDeadlock/chart.mmd +22 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterDeadlock/graph.dot +16 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterDeadlock/report.md +17 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterDeadlock/sany.out +2 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterDeadlock/verdict-1.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterDeadlock/verdict-1.out +56 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterDeadlock/verdict-2.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterDeadlock/verdict-2.out +77 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterFan/ChartRun.out +69 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterFan/CounterFan.cfg +2 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterFan/CounterFan.tla +11 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterFan/chart.mmd +85 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterFan/graph.dot +70 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterFan/report.md +12 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterFan/sany.out +2 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterFan/verdict-1.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterFan/verdict-1.out +67 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterFan/verdict-2.out +57 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterGuard/ChartRun.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterGuard/ChartRun.out +85 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterGuard/CounterGuard.cfg +6 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterGuard/CounterGuard.tla +13 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterGuard/chart.mmd +5 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterGuard/graph.dot +10 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterGuard/report.md +31 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterGuard/sany.out +2 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterGuard/verdict-1.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterGuard/verdict-1.out +64 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterGuard/verdict-2.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterGuard/verdict-2.out +77 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterInitPast/ChartRun.out +55 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterInitPast/CounterInitPast.cfg +3 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterInitPast/CounterInitPast.tla +10 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterInitPast/chart.mmd +12 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterInitPast/graph.dot +6 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterInitPast/report.md +12 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterInitPast/sany.out +2 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterInitPast/verdict-1.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterInitPast/verdict-1.out +33 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterInitPast/verdict-2.out +54 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterInitPastHolds/ChartRun.out +54 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterInitPastHolds/CounterInitPastHolds.cfg +3 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterInitPastHolds/CounterInitPastHolds.tla +10 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterInitPastHolds/chart.mmd +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterInitPastHolds/graph.dot +6 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterInitPastHolds/report.md +7 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterInitPastHolds/sany.out +2 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterInitPastHolds/verdict-1.out +46 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterInstance/ChartRun.out +59 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterInstance/CounterInstance.cfg +2 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterInstance/CounterInstance.tla +10 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterInstance/CounterSteps.tla +6 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterInstance/chart.mmd +17 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterInstance/graph.dot +15 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterInstance/report.md +14 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterInstance/sany.out +3 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterInstance/verdict-1.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterInstance/verdict-1.out +78 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterJump/ChartRun.out +59 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterJump/CounterJump.cfg +3 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterJump/CounterJump.tla +19 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterJump/chart.mmd +26 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterJump/graph.dot +22 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterJump/report.md +12 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterJump/sany.out +2 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterJump/verdict-1.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterJump/verdict-1.out +58 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterJump/verdict-2.out +57 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterLoop/ChartRun.out +57 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterLoop/CounterLoop.cfg +2 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterLoop/CounterLoop.tla +11 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterLoop/chart.mmd +17 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterLoop/graph.dot +15 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterLoop/report.md +14 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterLoop/sany.out +2 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterLoop/verdict-1.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterLoop/verdict-1.out +76 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterOrder/ChartRun.out +59 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterOrder/CounterOrder.cfg +3 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterOrder/CounterOrder.tla +11 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterOrder/chart.mmd +19 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterOrder/graph.dot +10 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterOrder/report.md +15 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterOrder/sany.out +2 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterOrder/verdict-1.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterOrder/verdict-1.out +56 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterOrder/verdict-2.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterOrder/verdict-2.out +67 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterRules/ChartRun.out +62 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterRules/CounterRules.cfg +6 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterRules/CounterRules.tla +24 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterRules/Moves.tla +5 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterRules/chart.mmd +22 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterRules/graph.dot +10 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterRules/report.md +23 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterRules/sany.out +3 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterRules/verdict-1.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterRules/verdict-1.out +58 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterRules/verdict-2.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterRules/verdict-2.out +66 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterRules/verdict-3.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterRules/verdict-3.out +74 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterRules/verdict-4.out +59 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterStart/ChartRun.out +58 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterStart/CounterStart.cfg +3 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterStart/CounterStart.tla +15 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterStart/chart.mmd +18 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterStart/graph.dot +13 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterStart/report.md +11 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterStart/sany.out +2 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterStart/verdict-1.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterStart/verdict-1.out +33 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterStart/verdict-2.out +57 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterStop/ChartRun.out +57 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterStop/CounterStop.cfg +3 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterStop/CounterStop.tla +15 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterStop/chart.mmd +7 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterStop/graph.dot +13 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterStop/report.md +6 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterStop/sany.out +2 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterStop/verdict-1.out +49 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterTie/ChartRun.out +61 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterTie/CounterTie.cfg +4 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterTie/CounterTie.tla +14 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterTie/chart.mmd +30 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterTie/graph.dot +19 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterTie/report.md +27 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterTie/sany.out +2 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterTie/verdict-1.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterTie/verdict-1.out +56 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterTie/verdict-2.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterTie/verdict-2.out +64 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterTie/verdict-3.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterTie/verdict-3.out +87 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterTie/verdict-4.out +57 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterTieInGraph/ChartRun.out +60 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterTieInGraph/CounterTieInGraph.cfg +3 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterTieInGraph/CounterTieInGraph.tla +12 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterTieInGraph/chart.mmd +23 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterTieInGraph/graph.dot +19 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterTieInGraph/report.md +11 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterTieInGraph/sany.out +2 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterTieInGraph/verdict-1.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterTieInGraph/verdict-1.out +56 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterTieInGraph/verdict-2.out +57 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterView/ChartRun.out +61 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterView/CounterView.cfg +4 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterView/CounterView.tla +14 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterView/chart.mmd +23 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterView/graph.dot +19 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterView/report.md +27 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterView/sany.out +2 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterView/verdict-1.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterView/verdict-1.out +56 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterView/verdict-2.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterView/verdict-2.out +64 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterView/verdict-3.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterView/verdict-3.out +87 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterView/verdict-4.out +57 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/CounterView/view.mjs +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/DieHard/ChartRun.out +59 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/DieHard/DieHard.cfg +4 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/DieHard/DieHard.tla +38 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/DieHard/chart.mmd +92 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/DieHard/graph.dot +126 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/DieHard/report.md +17 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/DieHard/sany.out +2 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/DieHard/verdict-1.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/DieHard/verdict-1.out +91 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/DieHard/verdict-2.out +57 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/FirstRule/ChartRun.out +61 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/FirstRule/FirstRule.cfg +6 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/FirstRule/FirstRule.tla +11 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/FirstRule/chart.mmd +22 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/FirstRule/graph.dot +16 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/FirstRule/report.md +25 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/FirstRule/sany.out +2 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/FirstRule/verdict-1.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/FirstRule/verdict-1.out +56 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/FirstRule/verdict-2.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/FirstRule/verdict-2.out +64 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/FirstRule/verdict-3.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/FirstRule/verdict-3.out +64 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/FirstRule/verdict-4.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/FirstRule/verdict-4.out +77 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/HiddenClock/ChartRun.out +57 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/HiddenClock/HiddenClock.cfg +4 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/HiddenClock/HiddenClock.tla +11 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/HiddenClock/chart.mmd +13 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/HiddenClock/graph.dot +12 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/HiddenClock/report.md +11 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/HiddenClock/sany.out +2 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/HiddenClock/verdict-1.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/HiddenClock/verdict-1.out +72 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/Login/ChartRun.out +57 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/Login/Login.cfg +3 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/Login/Login.tla +62 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/Login/chart.mmd +27 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/Login/graph.dot +40 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/Login/report.md +7 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/Login/sany.out +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/Login/verdict-1.out +47 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/Login/view.mjs +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/LoginGuarded/ChartRun.out +57 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/LoginGuarded/LoginGuarded.cfg +2 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/LoginGuarded/LoginGuarded.tla +64 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/LoginGuarded/chart.mmd +28 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/LoginGuarded/graph.dot +37 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/LoginGuarded/report.md +8 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/LoginGuarded/sany.out +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/LoginGuarded/verdict-1.out +47 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/LoginGuarded/view.mjs +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/LoginParallel/ChartRun.out +60 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/LoginParallel/LoginParallel.cfg +3 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/LoginParallel/LoginParallel.tla +64 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/LoginParallel/chart.mmd +44 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/LoginParallel/graph.dot +58 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/LoginParallel/report.md +17 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/LoginParallel/sany.out +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/LoginParallel/verdict-1.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/LoginParallel/verdict-1.out +83 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/LoginParallel/verdict-2.out +57 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/LoginParallel/view.mjs +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayer/ChartRun.out +60 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayer/MediaPlayer.cfg +3 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayer/MediaPlayer.tla +103 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayer/chart.mmd +43 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayer/graph.dot +93 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayer/report.md +14 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayer/sany.out +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayer/verdict-1.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayer/verdict-1.out +59 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayer/verdict-2.out +72 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayer/view.mjs +3 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayerFixed/ChartRun.out +57 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayerFixed/MediaPlayerFixed.cfg +2 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayerFixed/MediaPlayerFixed.tla +100 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayerFixed/chart.mmd +31 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayerFixed/graph.dot +85 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayerFixed/report.md +9 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayerFixed/sany.out +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayerFixed/verdict-1.out +47 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayerFixed/view.mjs +3 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayerProps/ChartRun.out +59 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayerProps/MediaPlayer.tla +103 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayerProps/MediaPlayerProps.cfg +3 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayerProps/MediaPlayerProps.tla +6 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayerProps/chart.mmd +42 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayerProps/graph.dot +93 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayerProps/report.md +19 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayerProps/sany.out +2 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayerProps/verdict-1.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayerProps/verdict-1.out +61 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayerProps/verdict-2.out +59 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayerProps/verdict-3.out +61 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayerProps/verdict-4.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayerProps/verdict-4.out +66 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayerProps/verdict-5.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayerProps/verdict-5.out +58 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayerProps/verdict-6.out +49 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/MediaPlayerProps/view.mjs +3 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/PickWorker/ChartRun.out +57 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/PickWorker/PickWorker.cfg +6 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/PickWorker/PickWorker.tla +11 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/PickWorker/chart.mmd +5 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/PickWorker/graph.dot +11 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/PickWorker/report.md +7 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/PickWorker/sany.out +5 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/PickWorker/verdict-1.out +70 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/Shadow/ChartRun.out +61 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/Shadow/Shadow.cfg +4 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/Shadow/Shadow.tla +11 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/Shadow/ShadowLib.tla +6 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/Shadow/ShadowSub.tla +3 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/Shadow/chart.mmd +9 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/Shadow/graph.dot +16 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/Shadow/report.md +8 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/Shadow/sany.out +4 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/Shadow/verdict-1.out +65 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/SpacedNames/ChartRun.out +59 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/SpacedNames/SpacedNames.cfg +2 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/SpacedNames/SpacedNames.tla +9 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/SpacedNames/chart.mmd +23 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/SpacedNames/graph.dot +17 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/SpacedNames/report.md +15 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/SpacedNames/sany.out +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/SpacedNames/verdict-1.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/SpacedNames/verdict-1.out +65 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/SpacedNames/verdict-2.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/SpacedNames/verdict-2.out +75 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/TwoPhase/ChartRun.out +69 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/TwoPhase/TwoPhase.cfg +3 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/TwoPhase/TwoPhase.tla +75 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/TwoPhase/chart.mmd +60 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/TwoPhase/graph.dot +297 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/TwoPhase/report.md +20 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/TwoPhase/sany.out +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/TwoPhase/verdict-1.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/TwoPhase/verdict-1.out +95 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/TwoPhase/verdict-2.out +57 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/TwoPhase/view.mjs +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/TwoPhaseFixed/ChartRun.out +57 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/TwoPhaseFixed/TwoPhaseFixed.cfg +3 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/TwoPhaseFixed/TwoPhaseFixed.tla +75 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/TwoPhaseFixed/chart.mmd +37 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/TwoPhaseFixed/graph.dot +223 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/TwoPhaseFixed/report.md +11 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/TwoPhaseFixed/sany.out +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/TwoPhaseFixed/verdict-1.out +47 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/TwoPhaseFixed/view.mjs +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/TypeGuard/ChartRun.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/TypeGuard/ChartRun.out +75 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/TypeGuard/TypeGuard.cfg +5 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/TypeGuard/TypeGuard.tla +11 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/TypeGuard/chart.mmd +18 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/TypeGuard/graph.dot +14 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/TypeGuard/report.md +29 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/TypeGuard/sany.out +2 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/TypeGuard/verdict-1.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/TypeGuard/verdict-1.out +64 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/TypeGuard/verdict-2.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/TypeGuard/verdict-2.out +75 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/UserNoDeadlock/ChartRun.out +58 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/UserNoDeadlock/UserNoDeadlock.cfg +2 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/UserNoDeadlock/UserNoDeadlock.tla +8 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/UserNoDeadlock/chart.mmd +22 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/UserNoDeadlock/graph.dot +16 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/UserNoDeadlock/report.md +15 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/UserNoDeadlock/sany.out +2 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/UserNoDeadlock/verdict-1.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/UserNoDeadlock/verdict-1.out +56 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/UserNoDeadlock/verdict-2.exit +1 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus/UserNoDeadlock/verdict-2.out +77 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/corpus.test.ts +403 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/differential.test.ts +565 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/grid/Grid.cfg +2 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/grid/Grid.tla +9 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/grid/sany.out +2 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/java.ts +63 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/labels.ts +643 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/messages.test.ts +212 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/pipeline.test.ts +270 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/plain-tlc.test.ts +255 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/render.test.ts +81 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/tlc-out.test.ts +90 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/test/tlc.ts +158 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/tsconfig.json +11 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/worker.mjs +32 -0
- package/builtin/skills/pstack/dsh/skills/statechart/scripts/worker.ts +35 -0
- package/builtin/skills/pstack/dsh/skills/swarm/SKILL.md +44 -0
- package/builtin/skills/pstack/dsh/skills/tdd/SKILL.md +45 -0
- package/builtin/skills/pstack/dsh/skills/teach/SKILL.md +24 -0
- package/builtin/skills/pstack/dsh/skills/technical-writing/SKILL.md +109 -0
- package/builtin/skills/pstack/dsh/skills/typescript-best-practices/SKILL.md +34 -0
- package/builtin/skills/pstack/dsh/skills/typescript-best-practices/references/patterns.md +328 -0
- package/builtin/skills/pstack/dsh/skills/unslop/SKILL.md +65 -0
- package/builtin/skills/pstack/dsh/skills/why/SKILL.md +99 -0
- package/builtin/skills/pstack/dsh/skills/why/references/epistemics.md +71 -0
- package/builtin/skills/pstack/dsh/skills/why/references/investigator-prompt.md +84 -0
- package/builtin/skills/pstack/dsh/skills/why/references/source-playbook.md +17 -0
- package/builtin/skills/pstack/dsh/skills/why/references/sources/code-archaeology.md +51 -0
- package/builtin/skills/pstack/dsh/skills/why/references/sources/databricks.md +70 -0
- package/builtin/skills/pstack/dsh/skills/why/references/sources/datadog.md +97 -0
- package/builtin/skills/pstack/dsh/skills/why/references/sources/incident-postmortem.md +5 -0
- package/builtin/skills/pstack/dsh/skills/why/references/sources/linear.md +48 -0
- package/builtin/skills/pstack/dsh/skills/why/references/sources/notion.md +55 -0
- package/builtin/skills/pstack/dsh/skills/why/references/sources/sentry.md +66 -0
- package/builtin/skills/pstack/dsh/skills/why/references/sources/slack.md +17 -0
- package/builtin/skills/pstack/dsh/skills/why/references/synthesizer-prompt.md +129 -0
- package/client/index.js +350 -85
- package/cordis.patch.yml +5 -13
- package/docs/Acknowledgements.md +24 -12
- package/docs/Changelog.md +27 -0
- package/docs/Compatibility.md +66 -3
- package/docs/EvaluationResults.md +8 -6
- package/docs/UsageGuide.md +61 -7
- package/docs/en/Acknowledgements.md +24 -12
- package/docs/en/Changelog.md +27 -0
- package/docs/en/Compatibility.md +66 -3
- package/docs/en/EvaluationResults.md +8 -6
- package/docs/en/UsageGuide.md +61 -7
- package/docs/images/agent-tree-preview.png +0 -0
- package/docs/images/agent-tree.png +0 -0
- package/docs/images/install-tgz-preview.png +0 -0
- package/docs/images/install-tgz.png +0 -0
- package/docs/images/npm-downloads-dark.svg +22 -0
- package/docs/images/npm-downloads.svg +22 -0
- package/docs/images/preset-settings-preview.png +0 -0
- package/docs/images/star-history-dark.svg +22 -0
- package/docs/images/star-history.svg +22 -0
- package/docs/images/task-status-preview.png +0 -0
- package/docs/images/task-status.png +0 -0
- package/lib/connection/approvals.d.ts +4 -0
- package/lib/connection/approvals.d.ts.map +1 -1
- package/lib/connection/approvals.js.map +1 -1
- package/lib/connection/client.d.ts.map +1 -1
- package/lib/connection/client.js +20 -3
- package/lib/connection/client.js.map +1 -1
- package/lib/connection/git.d.ts.map +1 -1
- package/lib/connection/git.js +17 -12
- package/lib/connection/git.js.map +1 -1
- package/lib/connection/http.d.ts.map +1 -1
- package/lib/connection/http.js +20 -1
- package/lib/connection/http.js.map +1 -1
- package/lib/connection/service.d.ts +3 -2
- package/lib/connection/service.d.ts.map +1 -1
- package/lib/connection/service.js +8 -3
- package/lib/connection/service.js.map +1 -1
- package/lib/connection/store.d.ts +1 -0
- package/lib/connection/store.d.ts.map +1 -1
- package/lib/connection/store.js +84 -35
- package/lib/connection/store.js.map +1 -1
- package/lib/connection/tasks.d.ts +4 -2
- package/lib/connection/tasks.d.ts.map +1 -1
- package/lib/connection/tasks.js +47 -42
- package/lib/connection/tasks.js.map +1 -1
- package/lib/connection/types.d.ts +2 -1
- package/lib/connection/types.d.ts.map +1 -1
- package/lib/core/batch-policy.js +2 -2
- package/lib/core/batch-policy.js.map +1 -1
- package/lib/core/delegation.d.ts +3 -0
- package/lib/core/delegation.d.ts.map +1 -1
- package/lib/core/delegation.js +2 -2
- package/lib/core/delegation.js.map +1 -1
- package/lib/core/deletion-command.d.ts.map +1 -1
- package/lib/core/deletion-command.js +192 -19
- package/lib/core/deletion-command.js.map +1 -1
- package/lib/core/tool-evidence.d.ts.map +1 -1
- package/lib/core/tool-evidence.js +9 -2
- package/lib/core/tool-evidence.js.map +1 -1
- package/lib/dsh/budget-settings.d.ts +11 -0
- package/lib/dsh/budget-settings.d.ts.map +1 -1
- package/lib/dsh/budget-settings.js +43 -9
- package/lib/dsh/budget-settings.js.map +1 -1
- package/lib/dsh/budgets.d.ts +5 -0
- package/lib/dsh/budgets.d.ts.map +1 -1
- package/lib/dsh/budgets.js +15 -0
- package/lib/dsh/budgets.js.map +1 -1
- package/lib/dsh/bundled-skills.d.ts +17 -0
- package/lib/dsh/bundled-skills.d.ts.map +1 -0
- package/lib/dsh/bundled-skills.js +94 -0
- package/lib/dsh/bundled-skills.js.map +1 -0
- package/lib/dsh/connection-approval.d.ts.map +1 -1
- package/lib/dsh/connection-approval.js +24 -2
- package/lib/dsh/connection-approval.js.map +1 -1
- package/lib/dsh/connection-history.d.ts +2 -1
- package/lib/dsh/connection-history.d.ts.map +1 -1
- package/lib/dsh/connection-history.js +22 -4
- package/lib/dsh/connection-history.js.map +1 -1
- package/lib/dsh/connection-host.d.ts +0 -1
- package/lib/dsh/connection-host.d.ts.map +1 -1
- package/lib/dsh/connection-host.js +59 -26
- package/lib/dsh/connection-host.js.map +1 -1
- package/lib/dsh/connection.d.ts.map +1 -1
- package/lib/dsh/connection.js +10 -5
- package/lib/dsh/connection.js.map +1 -1
- package/lib/dsh/directory-metadata.d.ts +4 -0
- package/lib/dsh/directory-metadata.d.ts.map +1 -0
- package/lib/dsh/directory-metadata.js +4 -0
- package/lib/dsh/directory-metadata.js.map +1 -0
- package/lib/dsh/index.d.ts.map +1 -1
- package/lib/dsh/index.js +2 -0
- package/lib/dsh/index.js.map +1 -1
- package/lib/dsh/memory-library.d.ts.map +1 -1
- package/lib/dsh/memory-library.js +8 -4
- package/lib/dsh/memory-library.js.map +1 -1
- package/lib/dsh/official-budgets.d.ts.map +1 -1
- package/lib/dsh/official-budgets.js +3 -2
- package/lib/dsh/official-budgets.js.map +1 -1
- package/lib/dsh/openspec-shell.d.ts +3 -0
- package/lib/dsh/openspec-shell.d.ts.map +1 -0
- package/lib/dsh/openspec-shell.js +25 -0
- package/lib/dsh/openspec-shell.js.map +1 -0
- package/lib/dsh/preset-declaration.d.ts +3 -0
- package/lib/dsh/preset-declaration.d.ts.map +1 -1
- package/lib/dsh/preset-declaration.js +39 -36
- package/lib/dsh/preset-declaration.js.map +1 -1
- package/lib/dsh/preset-files.d.ts.map +1 -1
- package/lib/dsh/preset-files.js +16 -1
- package/lib/dsh/preset-files.js.map +1 -1
- package/lib/dsh/preset-installation.d.ts +1 -0
- package/lib/dsh/preset-installation.d.ts.map +1 -1
- package/lib/dsh/preset-installation.js +5 -3
- package/lib/dsh/preset-installation.js.map +1 -1
- package/lib/dsh/preset-metadata.d.ts.map +1 -1
- package/lib/dsh/preset-metadata.js +18 -6
- package/lib/dsh/preset-metadata.js.map +1 -1
- package/lib/dsh/preset-recovery-runtime.d.ts.map +1 -1
- package/lib/dsh/preset-recovery-runtime.js +34 -12
- package/lib/dsh/preset-recovery-runtime.js.map +1 -1
- package/lib/dsh/preset-recovery.d.ts.map +1 -1
- package/lib/dsh/preset-recovery.js +79 -13
- package/lib/dsh/preset-recovery.js.map +1 -1
- package/lib/dsh/preset-version.d.ts +1 -0
- package/lib/dsh/preset-version.d.ts.map +1 -1
- package/lib/dsh/preset-version.js +1 -1
- package/lib/dsh/preset-version.js.map +1 -1
- package/lib/dsh/ptc-budget.d.ts.map +1 -1
- package/lib/dsh/ptc-budget.js +4 -2
- package/lib/dsh/ptc-budget.js.map +1 -1
- package/lib/dsh/skill-directory.d.ts +45 -0
- package/lib/dsh/skill-directory.d.ts.map +1 -0
- package/lib/dsh/skill-directory.js +113 -0
- package/lib/dsh/skill-directory.js.map +1 -0
- package/lib/dsh/skill-installation.d.ts +8 -0
- package/lib/dsh/skill-installation.d.ts.map +1 -0
- package/lib/dsh/skill-installation.js +131 -0
- package/lib/dsh/skill-installation.js.map +1 -0
- package/lib/dsh/skill-packages.d.ts +6 -0
- package/lib/dsh/skill-packages.d.ts.map +1 -0
- package/lib/dsh/skill-packages.js +8 -0
- package/lib/dsh/skill-packages.js.map +1 -0
- package/lib/dsh/super-code.d.ts.map +1 -1
- package/lib/dsh/super-code.js +143 -166
- package/lib/dsh/super-code.js.map +1 -1
- package/lib/dsh/tool-contracts.d.ts +53 -0
- package/lib/dsh/tool-contracts.d.ts.map +1 -0
- package/lib/dsh/tool-contracts.js +129 -0
- package/lib/dsh/tool-contracts.js.map +1 -0
- package/lib/dsh/windows-shell.d.ts +2 -2
- package/lib/dsh/windows-shell.d.ts.map +1 -1
- package/lib/dsh/windows-shell.js +11 -1
- package/lib/dsh/windows-shell.js.map +1 -1
- package/lib/long-memory.d.ts +2 -2
- package/lib/long-memory.d.ts.map +1 -1
- package/lib/long-memory.js +16 -9
- package/lib/long-memory.js.map +1 -1
- package/lib/memory-usage-projection.d.ts.map +1 -1
- package/lib/memory-usage-projection.js +13 -9
- package/lib/memory-usage-projection.js.map +1 -1
- package/lib/task-feedback-projection.d.ts +1 -0
- package/lib/task-feedback-projection.d.ts.map +1 -1
- package/lib/task-feedback-projection.js +65 -21
- package/lib/task-feedback-projection.js.map +1 -1
- package/lib/task-memory-store.d.ts.map +1 -1
- package/lib/task-memory-store.js +7 -1
- package/lib/task-memory-store.js.map +1 -1
- package/package.json +24 -4
- package/presets/super-code/agent.cordis.yml +4 -6
- package/runtime/skills/openspec/bin/openspec +2 -0
- package/runtime/skills/openspec/bin/openspec.cmd +6 -0
- package/presets/super-code/skills/super-code-frontend/SKILL.md +0 -33
- package/presets/super-code/skills/super-code-frontend/references/browser-validation.md +0 -59
- package/presets/super-code/skills/super-code-frontend/references/interaction-states.md +0 -51
- package/presets/super-code/skills/super-code-frontend/references/visual-design.md +0 -43
|
@@ -0,0 +1,726 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Media discovery and local delivery for design agents. Node 22+, no npm dependencies.
|
|
3
|
+
import fs from 'node:fs/promises';
|
|
4
|
+
import { existsSync, realpathSync } from 'node:fs';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
9
|
+
import { promisify } from 'node:util';
|
|
10
|
+
import { execFile } from 'node:child_process';
|
|
11
|
+
|
|
12
|
+
const exec = promisify(execFile);
|
|
13
|
+
const UA = 'oil-ui-curate-media/2.1 (+https://github.com/oil-oil/oil-ui; Powered by Poly Haven)';
|
|
14
|
+
const KINDS = ['all', 'texture', 'design', 'photo', 'blueprint', 'map', 'plate', 'science', 'video'];
|
|
15
|
+
const PREFIX = { polyhaven: 'T', vam: 'V', loc: 'L', wellcome: 'W', nasa: 'N', commons: 'C', met: 'M', aic: 'A', cleveland: 'K', japan: 'J', rijks: 'R' };
|
|
16
|
+
const USES = { page: 0, check: 1, reference: 2 };
|
|
17
|
+
const EXAMPLE = 'node scripts/curate-media.mjs search "wood" --kind texture --limit 6';
|
|
18
|
+
const HELP = `常用命令:
|
|
19
|
+
${EXAMPLE}
|
|
20
|
+
node scripts/curate-media.mjs search "<地名> | <当地语言的地名> | <物名> <年代>" --sheet --dir candidates
|
|
21
|
+
node scripts/curate-media.mjs search "bridge" --kind blueprint --sheet --dir candidates
|
|
22
|
+
node scripts/curate-media.mjs search "earth" --kind video --limit 4
|
|
23
|
+
node scripts/curate-media.mjs preview V1,T2 --sheet --dir candidates
|
|
24
|
+
node scripts/curate-media.mjs fetch C1,T2 --dir assets --sheet
|
|
25
|
+
node scripts/curate-media.mjs fetch V3 --dir <不上线的参考图目录>
|
|
26
|
+
node scripts/curate-media.mjs fetch T2 --maps all --resolution 1k --dir assets
|
|
27
|
+
node scripts/curate-media.mjs fetch "https://example.org/image.jpg" --out assets/image.jpg --note "来源说明"
|
|
28
|
+
|
|
29
|
+
用法:node curate-media.mjs <search|preview|fetch> <关键词或编号/URL> [选项]
|
|
30
|
+
search "<关键词>" 搜索并返回 JSON;用 | 分隔可一次搜几组词
|
|
31
|
+
先搜题材自己的地名、物名和专有名词(也试当地语言的叫法),每组一两个词,再加媒介或年代
|
|
32
|
+
日本馆藏用日文汉字,简体搜不到时换繁体或日文写法
|
|
33
|
+
preview <编号,...> 从缓存制作候选联系表,也可传图片 URL
|
|
34
|
+
fetch <编号,...> 批量下载;也可传一个或多个 HTTP(S) URL(URL 含逗号时单独传入)
|
|
35
|
+
|
|
36
|
+
--kind <类别> ${KINDS.join('|')};默认 all
|
|
37
|
+
--limit <1..30> 总结果数,all 默认 12、其他默认 6;all 交错分配各信源
|
|
38
|
+
--use <范围> all|page;默认 all,page 只留可以放进页面的条目
|
|
39
|
+
--resolution <分辨率> 贴图 JPG:1k|2k|4k|8k,默认 1k,缺失时返回实际可用分辨率
|
|
40
|
+
--maps <名称,...> fetch 贴图:diffuse|roughness|normal|all;默认 diffuse
|
|
41
|
+
--dir <目录> 下载/联系表目录,默认 assets
|
|
42
|
+
--out <文件> 单文件下载路径,与 --dir 互斥;已有文件不会覆盖
|
|
43
|
+
--sheet search/preview 下载预览,出联系表 PNG 和 HTML,并给每项算 look;fetch 出本地联系表并完整解码检查
|
|
44
|
+
--cache <文件> 默认当前工作目录 .curate-cache.json;跨命令使用同一缓存
|
|
45
|
+
--refresh search 忽略 24 小时的 Poly Haven 目录缓存
|
|
46
|
+
--timeout <毫秒> 单个 API 请求超时,默认 12000
|
|
47
|
+
--download-timeout <毫秒> 每文件下载超时,默认 180000;可设 1000..900000
|
|
48
|
+
--max-mb <1..2048> 每文件下载上限,默认 256 MB
|
|
49
|
+
--video-size <版本> medium|small|preview|orig,默认 medium;失败时回退较小版本并记录原因
|
|
50
|
+
--note <说明> fetch 附加来源说明;URL 下载的权利状态记为未核实
|
|
51
|
+
--help, -h 打印帮助;参数可用 --名字=值 或 --名字 值
|
|
52
|
+
|
|
53
|
+
信源与类别:texture=Poly Haven 无缝材质;design=Met、AIC、Cleveland、Rijksmuseum、V&A、Japan Search(日本国立博物馆、国会图书馆等)馆藏与 Poly Haven 3D 模型预览;
|
|
54
|
+
photo=Wikimedia Commons + LOC + NASA;blueprint=LOC hh/HABS-HAER 测绘图;map=LOC maps + Commons;
|
|
55
|
+
plate=Wellcome + Japan Search + Commons 版画;science=NASA + Wellcome;video=NASA;all 覆盖以上类别。
|
|
56
|
+
标题和说明里一个关键词都不含的条目会被滤掉,数量记在 filtered。
|
|
57
|
+
每项的 use 说明能怎么用:page=授权允许放进商业页面(CC BY/BY-SA 要署名);
|
|
58
|
+
check=权利没写清,先看来源页;reference=不能放进页面,可以下载到不上线的目录给生图当参考,
|
|
59
|
+
只取题材、年代、构图和材质线索,不让生成结果复刻原图。结果按 page、check、reference 排序。
|
|
60
|
+
--sheet 时每项另有 look:tone 明暗、mono 是否近单色、edge 四边平均色、edgeFlat 四边是否平整、
|
|
61
|
+
paper 是否浅色平整纸底(可 multiply 贴到色块上)、calm 哪些区域干净可压字;
|
|
62
|
+
联系表 PNG 按编号标注,Agent 直接读这张图挑选。
|
|
63
|
+
贴图 imageUrl/textureUrl 是真实 Diffuse JPG;previewUrl 是渲染预览;
|
|
64
|
+
textureMaps 提供 Diffuse/Roughness/OpenGL Normal 直链、大小与 MD5。
|
|
65
|
+
模型返回渲染预览及 glTF 文件/依赖清单,fetch 默认只下载模型预览图。
|
|
66
|
+
编号持续累加,同一素材保留原编号;清除缓存后编号重新开始。
|
|
67
|
+
标准输出是 JSON,进度/错误在标准错误。部分成功在 errors 中列出,仍保留有效结果;
|
|
68
|
+
全部信源失败或下载有失败时返回码 1。正常零匹配返回码 0。
|
|
69
|
+
HTML 联系表打开后显示真实图片宽高和加载失败;远程预览不会下载视频。
|
|
70
|
+
下载默认检查传输长度、签名、贴图 MD5;有 ffprobe 时检查实际宽高。
|
|
71
|
+
--sheet、look 及视频 poster 需要 ffmpeg/ffprobe;缺少时保留下载并明确报告未完成项。
|
|
72
|
+
所有成功下载及视频首帧 poster 自动记入同目录 SOURCES.md。
|
|
73
|
+
馆藏与 NASA 权利按记录/官方条款输出,不将整个信源标为可自由商用。`;
|
|
74
|
+
|
|
75
|
+
function usage(message) { throw new Error(`${message}\n示例:${EXAMPLE}\n批量下载:node scripts/curate-media.mjs fetch V1,T2 --dir assets --sheet\n全部选项:node scripts/curate-media.mjs --help`); }
|
|
76
|
+
export function parseArgs(argv) {
|
|
77
|
+
const [command, ...args] = argv;
|
|
78
|
+
if (!command || command === '--help' || command === '-h' || args.includes('--help') || args.includes('-h')) return { help: true };
|
|
79
|
+
if (!['search', 'fetch', 'preview'].includes(command)) usage(`未知命令 ${command}`);
|
|
80
|
+
const allowed = {
|
|
81
|
+
search: ['kind', 'limit', 'use', 'resolution', 'dir', 'sheet', 'cache', 'refresh', 'timeout'],
|
|
82
|
+
preview: ['dir', 'sheet', 'cache', 'timeout'],
|
|
83
|
+
fetch: ['dir', 'out', 'sheet', 'cache', 'maps', 'resolution', 'timeout', 'max-mb', 'note', 'video-size', 'download-timeout'],
|
|
84
|
+
}[command];
|
|
85
|
+
const flags = ['sheet', 'refresh'];
|
|
86
|
+
const opts = {}, positional = [];
|
|
87
|
+
for (let i = 0; i < args.length; i++) {
|
|
88
|
+
const arg = args[i];
|
|
89
|
+
if (!arg.startsWith('-')) { positional.push(arg); continue; }
|
|
90
|
+
const match = arg.match(/^--([^=]+)(?:=(.*))?$/);
|
|
91
|
+
if (!match || !allowed.includes(match[1])) usage(`不支持 ${arg};${command} 支持:${allowed.map(k => '--' + k).join(' ')}`);
|
|
92
|
+
const [, key, inline] = match;
|
|
93
|
+
if (key in opts) usage(`选项 --${key} 只能写一次`);
|
|
94
|
+
if (flags.includes(key)) {
|
|
95
|
+
if (inline !== undefined) usage(`--${key} 是不带值的开关`);
|
|
96
|
+
opts[key] = true;
|
|
97
|
+
} else {
|
|
98
|
+
const value = inline ?? args[++i];
|
|
99
|
+
if (!value || value.startsWith('--')) usage(`--${key} 缺少值`);
|
|
100
|
+
opts[key] = value;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (!positional.length || !positional.join(' ').trim()) usage(`${command} 缺少关键词、编号或 URL`);
|
|
104
|
+
if (opts.out && opts.dir) usage('--out 与 --dir 不能同时使用');
|
|
105
|
+
opts.kind ??= 'all';
|
|
106
|
+
if (!KINDS.includes(opts.kind)) usage(`--kind 应为 ${KINDS.join('|')}`);
|
|
107
|
+
opts.use ??= 'all';
|
|
108
|
+
if (!['all', 'page'].includes(opts.use)) usage('--use 应为 all|page');
|
|
109
|
+
for (const [key, fallback, min, max] of [['limit', opts.kind === 'all' ? 12 : 6, 1, 30],['timeout', 12000, 1000, 120000], ['max-mb', 256, 1, 2048], ['download-timeout', 180000, 1000, 900000]]) {
|
|
110
|
+
opts[key] = Number(opts[key] ?? fallback);
|
|
111
|
+
if (!Number.isInteger(opts[key]) || opts[key] < min || opts[key] > max) usage(`--${key} 应为 ${min}..${max} 的整数`);
|
|
112
|
+
}
|
|
113
|
+
opts.resolution ??= '1k';
|
|
114
|
+
if (!['1k', '2k', '4k', '8k'].includes(opts.resolution)) usage('--resolution 应为 1k|2k|4k|8k');
|
|
115
|
+
opts['video-size'] ??= 'medium';
|
|
116
|
+
if (!['medium', 'small', 'preview', 'orig'].includes(opts['video-size'])) usage('--video-size 应为 medium|small|preview|orig');
|
|
117
|
+
opts.maps = (opts.maps ?? 'diffuse').split(',');
|
|
118
|
+
if (opts.maps.includes('all') && opts.maps.length === 1) opts.maps = ['diffuse', 'roughness', 'normal'];
|
|
119
|
+
if (opts.maps.some(k => !['diffuse', 'roughness', 'normal'].includes(k))) usage('--maps 应为 diffuse,roughness,normal 或 all');
|
|
120
|
+
opts.maps = [...new Set(opts.maps)];
|
|
121
|
+
opts.dir = path.resolve(opts.dir ?? (opts.out ? path.dirname(opts.out) : 'assets'));
|
|
122
|
+
const defaultCache = path.resolve('.curate-cache.json');
|
|
123
|
+
const fallbackCaches = [path.join(opts.dir, '.curate-cache.json'), path.join(path.dirname(opts.dir), '.curate-cache.json')];
|
|
124
|
+
opts.cache = opts.cache ? path.resolve(opts.cache) : (!existsSync(defaultCache) && fallbackCaches.find(c => existsSync(c))) || defaultCache;
|
|
125
|
+
return { command, positional, opts };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const https = value => String(value).replace(/^http:\/\//, 'https://').replace(/^\/\//, 'https://');
|
|
129
|
+
function httpUrl(value) {
|
|
130
|
+
const u = new URL(value);
|
|
131
|
+
if (!['http:', 'https:'].includes(u.protocol) || u.username || u.password) throw new Error('仅支持不带凭据的 HTTP(S) URL');
|
|
132
|
+
return u.href;
|
|
133
|
+
}
|
|
134
|
+
const apiUrl = (base, params) => { const u = new URL(base); for (const [k, v] of Object.entries(params)) u.searchParams.set(k, String(v)); return u.href; };
|
|
135
|
+
// AIC asks clients to identify themselves in its own header, for the API and IIIF alike.
|
|
136
|
+
const headersFor = url => ({ 'User-Agent': UA, ...(/^https:\/\/[^/]*artic\.edu\//.test(url) ? { 'AIC-User-Agent': UA } : {}) });
|
|
137
|
+
async function json(url, opts) {
|
|
138
|
+
const res = await fetch(httpUrl(url), { headers: { ...headersFor(url), Accept: 'application/json' }, signal: AbortSignal.timeout(opts.timeout) });
|
|
139
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}: ${url}`);
|
|
140
|
+
return res.json();
|
|
141
|
+
}
|
|
142
|
+
async function pool(values, fn, concurrency = 3) {
|
|
143
|
+
const output = new Array(values.length); let cursor = 0;
|
|
144
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, values.length) }, async () => {
|
|
145
|
+
while (cursor < values.length) { const i = cursor++; try { output[i] = { status: 'fulfilled', value: await fn(values[i], i) }; } catch (reason) { output[i] = { status: 'rejected', reason }; } }
|
|
146
|
+
}));
|
|
147
|
+
return output;
|
|
148
|
+
}
|
|
149
|
+
// Latin words of one or two letters are joiners (of, de, la, en); years, numbers and CJK words stay.
|
|
150
|
+
const JOINERS = new Set(['the', 'and', 'for', 'with', 'from', 'der', 'die', 'das', 'les', 'des', 'del', 'van', 'von']);
|
|
151
|
+
export const queryTerms = q => String(q).toLowerCase().split(/[^\p{L}\p{N}]+/u).filter(w => w && (/\d|[^\p{Script=Latin}]/u.test(w) || (w.length > 2 && !JOINERS.has(w))));
|
|
152
|
+
// Whole words with plurals; long terms also match compounds. Substrings made "fig" match "configuration"; plate captions write "fig. 2".
|
|
153
|
+
export function matchCount(text, terms) {
|
|
154
|
+
const flat = String(text ?? '').normalize('NFKC').toLowerCase(), list = flat.replace(/<[^>]*>/g, ' ').replace(/\b(figs?|pl|no|vol)\.\s*\d+/g, ' ').split(/[^\p{L}\p{N}]+/u).filter(Boolean);
|
|
155
|
+
// Adjacent pairs joined, so "tea pot" in a title answers "teapot".
|
|
156
|
+
const words = new Set([...list, ...list.slice(1).map((w, i) => list[i] + w)]);
|
|
157
|
+
// CJK text has no spaces between words, so CJK terms match as substrings.
|
|
158
|
+
const has = t => /[^\p{Script=Latin}\p{N}]/u.test(t) ? flat.includes(t) : words.has(t) || words.has(t + 's') || words.has(t + 'es') || (t.length > 3 && t.endsWith('s') && words.has(t.slice(0, -1))) || (t.length >= 5 && [...words].some(w => w.startsWith(t)));
|
|
159
|
+
return terms.filter(has).length;
|
|
160
|
+
}
|
|
161
|
+
// page: the licence allows a commercial page; reference: it does not, but the picture can guide generation; check: unclear.
|
|
162
|
+
export function rightsUse(license = '', licenseUrl = '') {
|
|
163
|
+
const s = `${license} ${licenseUrl}`.toLowerCase();
|
|
164
|
+
if (/non-?commercial|by-nc|\bnc\b|no ?deriv|by-nd|\bnd\b|requires licensing/.test(s)) return 'reference';
|
|
165
|
+
if (/third-party rights|not verified|not specified|in copyright/.test(s)) return 'check';
|
|
166
|
+
if (/cc0|publicdomain|public domain|\bpdm?\b|no known restrictions|not subject to us copyright|creativecommons\.org\/licenses\/by(-sa)?\/|\bcc[ -]by(-sa)?\b|attribution( |-sharealike )\d/.test(s)) return 'page';
|
|
167
|
+
return 'check';
|
|
168
|
+
}
|
|
169
|
+
const emptyCache = () => ({ version: 1, items: {}, catalogues: {} });
|
|
170
|
+
async function readCache(file) {
|
|
171
|
+
try {
|
|
172
|
+
const data = JSON.parse(await fs.readFile(file, 'utf8'));
|
|
173
|
+
if (data.version !== 1 || !data.items || !data.catalogues) throw new Error('格式或版本不支持');
|
|
174
|
+
return data;
|
|
175
|
+
} catch (e) {
|
|
176
|
+
if (e.code === 'ENOENT') return emptyCache();
|
|
177
|
+
throw new Error(`缓存 ${file} 无法读取:${e.message};请用 --cache 指定新文件`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
async function replaceCache(temp, file) {
|
|
181
|
+
const deadline = Date.now() + 5000;
|
|
182
|
+
for (;;) {
|
|
183
|
+
try { await fs.rename(temp, file); return; }
|
|
184
|
+
catch (error) {
|
|
185
|
+
// Windows readers/virus scanners can briefly deny replacing an existing
|
|
186
|
+
// file. Keep the old cache intact and retry only sharing/permission errors.
|
|
187
|
+
if (!['EPERM', 'EACCES', 'EBUSY'].includes(error.code) || Date.now() >= deadline) throw error;
|
|
188
|
+
await new Promise(resolve => setTimeout(resolve, 60));
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// Serialize read/modify/write across concurrent agent processes; network calls stay outside the lock.
|
|
193
|
+
async function updateCache(file, fn) {
|
|
194
|
+
await fs.mkdir(path.dirname(file), { recursive: true });
|
|
195
|
+
const lock = file + '.lock'; const deadline = Date.now() + 5000;
|
|
196
|
+
while (true) {
|
|
197
|
+
try { await fs.mkdir(lock); break; } catch (e) {
|
|
198
|
+
if (e.code !== 'EEXIST') throw e;
|
|
199
|
+
if (Date.now() > deadline) throw new Error(`缓存正在使用:${lock};若上次进程已退出,可移除锁目录后重试`);
|
|
200
|
+
await new Promise(resolve => setTimeout(resolve, 60));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
const temp = file + '.' + randomUUID() + '.tmp';
|
|
204
|
+
try {
|
|
205
|
+
const data = await readCache(file); const result = await fn(data);
|
|
206
|
+
await fs.writeFile(temp, JSON.stringify(data)); await replaceCache(temp, file); return result;
|
|
207
|
+
} finally { await fs.rm(temp, { force: true }); await fs.rmdir(lock); }
|
|
208
|
+
}
|
|
209
|
+
async function catalogue(type, opts) {
|
|
210
|
+
const data = await readCache(opts.cache); const cached = data.catalogues[type];
|
|
211
|
+
if (!opts.refresh && cached && Date.now() - cached.time < 86400000) return cached.data;
|
|
212
|
+
const fresh = await json(`https://api.polyhaven.com/assets?t=${type}`, opts);
|
|
213
|
+
await updateCache(opts.cache, c => { c.catalogues[type] = { time: Date.now(), data: fresh }; });
|
|
214
|
+
return fresh;
|
|
215
|
+
}
|
|
216
|
+
export function selectMap(files, keys, resolution) {
|
|
217
|
+
for (const key of keys) {
|
|
218
|
+
const group = files[key]; if (!group) continue;
|
|
219
|
+
const available = Object.keys(group).filter(r => group[r]?.jpg?.url).sort((a, b) => parseInt(a) - parseInt(b));
|
|
220
|
+
const r = available.includes(resolution) ? resolution : available.filter(r => parseInt(r) <= parseInt(resolution)).at(-1) ?? available[0];
|
|
221
|
+
if (r) return { ...group[r].jpg, resolution: r, channel: key };
|
|
222
|
+
}
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
async function resolveTexture(item, opts) {
|
|
226
|
+
const files = await json(`https://api.polyhaven.com/files/${encodeURIComponent(item.sourceId)}`, opts);
|
|
227
|
+
const textureMaps = {
|
|
228
|
+
diffuse: selectMap(files, ['Diffuse', 'diff', 'Color', 'col'], opts.resolution),
|
|
229
|
+
roughness: selectMap(files, ['Rough', 'rough', 'Roughness'], opts.resolution),
|
|
230
|
+
normal: selectMap(files, ['nor_gl', 'Normal', 'normal'], opts.resolution),
|
|
231
|
+
};
|
|
232
|
+
if (!textureMaps.diffuse) throw new Error(`${item.sourceId} 没有可用 Diffuse JPG`);
|
|
233
|
+
return { ...item, textureMaps, textureUrl: textureMaps.diffuse.url, imageUrl: textureMaps.diffuse.url, normalConvention: 'OpenGL', tiling: '查看 previewUrl 并检查重复边缘;部分材质仅单轴重复' };
|
|
234
|
+
}
|
|
235
|
+
async function searchPoly(query, limit, opts, type = 'textures') {
|
|
236
|
+
const data = await catalogue(type, opts), terms = queryTerms(query);
|
|
237
|
+
const ranked = Object.entries(data).map(([slug, meta]) => {
|
|
238
|
+
const hay = [slug.replaceAll('_', ' '), meta.name, meta.description, meta.category, ...(meta.categories ?? []), ...(meta.tags ?? [])].join(' ');
|
|
239
|
+
return { slug, meta, score: matchCount(hay, terms) };
|
|
240
|
+
}).filter(x => x.score >= Math.max(1, Math.ceil(terms.length / 2))).sort((a, b) => b.score - a.score).slice(0, limit);
|
|
241
|
+
const out = await pool(ranked, async ({ slug, meta }) => {
|
|
242
|
+
const previewUrl = `https://cdn.polyhaven.com/asset_img/thumbs/${encodeURIComponent(slug)}.png?width=768`;
|
|
243
|
+
const item = { matched: ranked.find(x => x.slug === slug).score, provider: 'polyhaven', sourceId: slug, kind: type === 'textures' ? 'texture' : 'model', title: meta.name ?? slug, detail: (meta.categories ?? []).join(', '), license: 'CC0', licenseUrl: 'https://polyhaven.com/license', attribution: [Object.keys(meta.authors ?? {}).join(', '), 'Powered by Poly Haven'].filter(Boolean).join(' · '), pageUrl: `https://polyhaven.com/a/${encodeURIComponent(slug)}`, previewUrl, imageUrl: previewUrl };
|
|
244
|
+
if (type === 'textures') return resolveTexture(item, opts);
|
|
245
|
+
const files = await json(`https://api.polyhaven.com/files/${encodeURIComponent(slug)}`, opts);
|
|
246
|
+
const model = files.gltf?.[opts.resolution]?.gltf ?? Object.values(files.gltf ?? {})[0]?.gltf;
|
|
247
|
+
return { ...item, modelFile: model ?? null, previewType: 'rendered-image' };
|
|
248
|
+
});
|
|
249
|
+
const errors = out.flatMap((r, i) => r.status === 'rejected' ? [`${ranked[i].slug}: ${r.reason.message}`] : []);
|
|
250
|
+
return { items: out.filter(r => r.status === 'fulfilled').map(r => r.value), errors };
|
|
251
|
+
}
|
|
252
|
+
function iiifBase(value) { return value?.replace(/\/info\.json(?:\?.*)?$/, '').replace(/\/full\/.*$/, '').replace(/\/$/, ''); }
|
|
253
|
+
async function searchVam(query, limit, opts) {
|
|
254
|
+
const data = await json(apiUrl('https://api.vam.ac.uk/v2/objects/search', { q: query, images_exist: 1, page_size: limit }), opts);
|
|
255
|
+
return (data.records ?? []).flatMap(r => {
|
|
256
|
+
const base = iiifBase(r._images?._iiif_image_base_url ?? r._images?._primary_thumbnail ?? (r._primaryImageId ? `https://framemark.vam.ac.uk/collections/${r._primaryImageId}` : null));
|
|
257
|
+
if (!base) return [];
|
|
258
|
+
return [{ provider: 'vam', sourceId: r.systemNumber, kind: 'design', title: r._primaryTitle || r.objectType || r.systemNumber, detail: [r.objectType, r._primaryMaker?.name, r._primaryDate, r._primaryPlace].filter(Boolean).join(' · '), license: 'V&A terms: permitted non-commercial uses only; commercial/home-page/high-resolution publication requires licensing', licenseUrl: 'https://www.vam.ac.uk/info/va-websites-terms-conditions', attribution: `© Victoria and Albert Museum, London (${r.systemNumber})`, pageUrl: `https://collections.vam.ac.uk/item/${r.systemNumber}`, iiifInfoUrl: `${base}/info.json`, imageUrl: `${base}/full/!1600,1600/0/default.jpg`, previewUrl: `${base}/full/!400,400/0/default.jpg` }];
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
async function searchWellcome(query, limit, opts, kind = 'plate') {
|
|
262
|
+
const data = await json(apiUrl('https://api.wellcomecollection.org/catalogue/v2/images', { query, pageSize: limit, include: 'source.contributors' }), opts);
|
|
263
|
+
return (data.results ?? []).flatMap(r => {
|
|
264
|
+
const loc = r.locations?.find(x => x.locationType?.id === 'iiif-image') ?? r.thumbnail;
|
|
265
|
+
const base = iiifBase(loc?.url); if (!base) return [];
|
|
266
|
+
const people = (r.source?.contributors ?? []).map(c => c.agent?.label).filter(Boolean);
|
|
267
|
+
return [{ provider: 'wellcome', sourceId: r.id, kind, title: r.source?.title ?? r.id, detail: people.join(', '), license: loc.license?.label ?? 'Rights not specified; check item', licenseUrl: loc.license?.url ?? `https://wellcomecollection.org/works/${r.source?.id}`, attribution: [...people, loc.credit || 'Wellcome Collection'].join(' · '), pageUrl: `https://wellcomecollection.org/works/${r.source?.id}`, iiifInfoUrl: `${base}/info.json`, imageUrl: `${base}/full/!1600,1600/0/default.jpg`, previewUrl: `${base}/full/!400,400/0/default.jpg` }];
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
function locItem(row, kind, imageUrl, previewUrl, sourceId, pageUrl) {
|
|
271
|
+
const rights = row.item?.rights_advisory ?? row.item?.rights_information ?? row.rights ?? 'Rights not specified in search; check Rights & Access on item page';
|
|
272
|
+
return { provider: 'loc', sourceId, kind, title: row.title ?? 'Library of Congress', detail: [row.date, ...(row.contributor ?? row.contributors ?? [])].filter(Boolean).join(' · '), license: Array.isArray(rights) ? rights.join(' ') : rights, licenseUrl: pageUrl, attribution: row.item?.repository ?? 'Library of Congress', pageUrl, imageUrl: https(imageUrl), previewUrl: https(previewUrl ?? imageUrl) };
|
|
273
|
+
}
|
|
274
|
+
async function searchLoc(query, limit, opts, kind = 'photo') {
|
|
275
|
+
if (kind === 'blueprint') return searchBlueprint(query, limit, opts);
|
|
276
|
+
const data = await json(apiUrl(`https://www.loc.gov/${kind === 'map' ? 'maps' : 'photos'}/`, { q: query, fo: 'json', c: Math.min(limit * 2, 60) }), opts);
|
|
277
|
+
return (data.results ?? []).filter(r => !r.access_restricted).flatMap(r => {
|
|
278
|
+
const urls = (r.image_url ?? []).filter(u => /\.(jpe?g|png)(?:[?#]|$)/i.test(u));
|
|
279
|
+
if (!urls.length) return [];
|
|
280
|
+
const best = urls.at(-1).split('#')[0], page = https(r.url || r.id);
|
|
281
|
+
return [locItem(r, kind, best, (urls[1] ?? urls[0]).split('#')[0], r.id ?? page, page)];
|
|
282
|
+
}).slice(0, limit);
|
|
283
|
+
}
|
|
284
|
+
async function searchBlueprint(query, limit, opts) {
|
|
285
|
+
// hh includes photographs and surveys. Only /sheet/ images are measured drawings.
|
|
286
|
+
const data = await json(apiUrl('https://www.loc.gov/collections/historic-american-buildings-landscapes-and-engineering-records/', { q: `${query} drawings`, fo: 'json', at: 'results', c: Math.min(limit * 3, 60) }), opts);
|
|
287
|
+
return (data.results ?? []).flatMap(row => {
|
|
288
|
+
const resource = (row.resources ?? []).find(r => /\/sheet\//.test(r.image ?? ''));
|
|
289
|
+
const image = resource?.image ?? (row.image_url ?? []).find(u => /\/sheet\//.test(u));
|
|
290
|
+
if (!image) return [];
|
|
291
|
+
const preview = new URL(https(image), 'https://www.loc.gov').href.split('#')[0];
|
|
292
|
+
// LOC service derivative suffixes: _150px thumbnail, r medium, v large.
|
|
293
|
+
const full = preview.replace(/_150px\.jpg$/, 'v.jpg').replace(/r\.jpg$/, 'v.jpg');
|
|
294
|
+
const page = https(resource?.url ?? row.url ?? row.id);
|
|
295
|
+
return [locItem(row, 'blueprint', full, preview.replace(/_150px\.jpg$/, 'r.jpg'), page, page)];
|
|
296
|
+
}).slice(0, limit);
|
|
297
|
+
}
|
|
298
|
+
export function selectNasaFile(urls, type, videoSize = 'medium') {
|
|
299
|
+
const candidates = urls.filter(u => type === 'video' ? /\.(mp4|mov)(?:\?|$)/i.test(u) : /\.(jpe?g|png)(?:\?|$)/i.test(u));
|
|
300
|
+
const rank = type === 'video' ? [`~${videoSize}.`, ...['~medium.', '~small.', '~mobile.', '~preview.', '~orig.'].filter(x => x !== `~${videoSize}.`)] : ['~orig.', '~large.', '~medium.', '~small.', '~thumb.'];
|
|
301
|
+
return candidates.sort((a, b) => {
|
|
302
|
+
const score = u => { const i = rank.findIndex(t => u.includes(t)); return i < 0 ? 99 : i; };
|
|
303
|
+
return score(a) - score(b);
|
|
304
|
+
})[0];
|
|
305
|
+
}
|
|
306
|
+
async function resolveNasa(item, opts) {
|
|
307
|
+
const data = await json(item.manifestUrl, opts);
|
|
308
|
+
const urls = Array.isArray(data) ? data : (data.collection?.items ?? []).map(r => r.href);
|
|
309
|
+
const url = selectNasaFile(urls, item.mediaType, opts['video-size']);
|
|
310
|
+
if (!url) throw new Error(`${item.sourceId} 无可下载的 ${item.mediaType} 文件`);
|
|
311
|
+
const alternates = item.mediaType === 'video' ? ['preview', 'small', 'mobile'].map(size => urls.find(u => u.includes(`~${size}.`) && /\.mp4$/i.test(u))).filter(Boolean) : ['large', 'medium', 'small'].map(size => urls.find(u => u.includes(`~${size}.`) && /\.(jpe?g|png)$/i.test(u))).filter(Boolean);
|
|
312
|
+
return { ...item, downloadCandidates: [...new Set([url, ...alternates].map(https))], ...(item.mediaType === 'video' ? { videoUrl: https(url) } : { imageUrl: https(url) }) };
|
|
313
|
+
}
|
|
314
|
+
async function searchNasa(query, limit, opts, mediaType = 'image', kind = 'science') {
|
|
315
|
+
const data = await json(apiUrl('https://images-api.nasa.gov/search', { q: query, media_type: mediaType, page_size: mediaType === 'video' ? Math.min(limit * 3, 100) : limit }), opts);
|
|
316
|
+
return (data.collection?.items ?? []).flatMap(row => {
|
|
317
|
+
const r = row.data?.[0]; if (!r) return [];
|
|
318
|
+
const image = row.links?.find(l => l.render === 'image' && l.rel === 'preview')?.href ?? row.links?.find(l => l.render === 'image')?.href;
|
|
319
|
+
if (!image) return [];
|
|
320
|
+
if (mediaType === 'video' && /animation|visualization|simulation|computer.generated|rendering/i.test([r.title, ...(r.keywords ?? [])].join(' '))) return [];
|
|
321
|
+
const thirdParty = /copyright|©|all rights reserved/i.test([r.description, r.photographer, r.secondary_creator].join(' '));
|
|
322
|
+
return [{ provider: 'nasa', sourceId: r.nasa_id, kind, mediaType: r.media_type, title: r.title ?? r.nasa_id, detail: [r.center, r.date_created, r.description?.replace(/<[^>]*>/g, '').slice(0, 500)].filter(Boolean).join(' · '), license: thirdParty ? 'Third-party rights mentioned; check item credits' : 'NASA media usage guidelines; generally not subject to US copyright; verify third-party credits', licenseUrl: 'https://www.nasa.gov/nasa-brand-center/images-and-media/', attribution: ['NASA', r.center, r.photographer, r.secondary_creator].filter(Boolean).join(' · '), pageUrl: `https://images.nasa.gov/details/${encodeURIComponent(r.nasa_id)}`, imageUrl: https(image), previewUrl: https(image), manifestUrl: https(row.href ?? `https://images-api.nasa.gov/asset/${encodeURIComponent(r.nasa_id)}`) }];
|
|
323
|
+
}).slice(0, limit);
|
|
324
|
+
}
|
|
325
|
+
const plain = s => String(s ?? '').replace(/</g, '<').replace(/>/g, '>').replace(/<[^>]*>/g, ' ').replace(/&/g, '&').replace(/"/g, '"').replace(/�?39;/g, "'").replace(/\s+/g, ' ').trim();
|
|
326
|
+
// Commons search matches anywhere on the file page; map and plate kinds keep only files whose title or description names the medium.
|
|
327
|
+
const MEDIUM = { map: /\b(map|maps|plan|chart|atlas|kaart|carte|karte|plattegrond)\b/i, plate: /\b(engraving|etching|lithograph|woodcut|plate|print|illustration|drawing|gravure)\b/i };
|
|
328
|
+
async function searchCommons(query, limit, opts, kind = 'photo') {
|
|
329
|
+
// Wikimedia only serves standard thumbnail widths; 500 and 1920 are among them.
|
|
330
|
+
const data = await json(apiUrl('https://commons.wikimedia.org/w/api.php', { action: 'query', format: 'json', formatversion: 2, generator: 'search', gsrsearch: `${query} filetype:bitmap`, gsrnamespace: 6, gsrlimit: Math.min(limit * 2, 40), prop: 'imageinfo', iiprop: 'url|size|mime|extmetadata', iiurlwidth: 1920, iiextmetadatafilter: 'LicenseShortName|License|LicenseUrl|Artist|DateTimeOriginal|ObjectName|ImageDescription|AttributionRequired' }), opts);
|
|
331
|
+
return (data.query?.pages ?? []).sort((a, b) => (a.index ?? 0) - (b.index ?? 0)).flatMap(p => {
|
|
332
|
+
const ii = p.imageinfo?.[0]; if (!ii?.url || !ii.width) return [];
|
|
333
|
+
const m = k => plain(ii.extmetadata?.[k]?.value);
|
|
334
|
+
const webSafe = /^image\/(jpeg|png|webp|gif)$/.test(ii.mime);
|
|
335
|
+
const imageUrl = ii.thumburl && (ii.width > 1920 || !webSafe) ? ii.thumburl : webSafe ? ii.url : null;
|
|
336
|
+
if (!imageUrl) return [];
|
|
337
|
+
const previewUrl = ii.thumburl && ii.width > 500 ? ii.thumburl.replace(/\/\d+px-/, '/500px-') : imageUrl;
|
|
338
|
+
const short = m('LicenseShortName') || m('License') || 'License on file page';
|
|
339
|
+
if (MEDIUM[kind] && !MEDIUM[kind].test(`${p.title} ${m('ObjectName')} ${m('ImageDescription')}`)) return [];
|
|
340
|
+
return [{ provider: 'commons', sourceId: String(p.pageid), kind, title: m('ObjectName') || p.title.replace(/^File:/, '').replace(/\.[a-z0-9]+$/i, ''), detail: [m('DateTimeOriginal'), m('Artist')].filter(Boolean).join(' · ').slice(0, 200), about: m('ImageDescription').slice(0, 300), license: short, licenseUrl: m('LicenseUrl') || ii.descriptionurl, attribution: [m('Artist') || 'Unknown', 'via Wikimedia Commons'].join(' · ').slice(0, 300), pageUrl: ii.descriptionurl, imageUrl: https(imageUrl), previewUrl: https(previewUrl), originalWidth: ii.width, originalHeight: ii.height }];
|
|
341
|
+
}).slice(0, limit * 2);
|
|
342
|
+
}
|
|
343
|
+
async function searchAic(query, limit, opts, kind = 'design') {
|
|
344
|
+
const data = await json(apiUrl('https://api.artic.edu/api/v1/artworks/search', { q: query, limit: Math.min(limit * 2, 40), fields: 'id,title,artist_display,date_display,image_id,is_public_domain,thumbnail,medium_display,artwork_type_title', 'query[term][is_public_domain]': 'true' }), opts);
|
|
345
|
+
const iiif = data.config?.iiif_url ?? 'https://www.artic.edu/iiif/2';
|
|
346
|
+
return (data.data ?? []).flatMap(r => {
|
|
347
|
+
if (!r.image_id || !r.is_public_domain) return [];
|
|
348
|
+
const width = r.thumbnail?.width, height = r.thumbnail?.height;
|
|
349
|
+
const base = `${iiif}/${r.image_id}/full`;
|
|
350
|
+
return [{ provider: 'aic', sourceId: String(r.id), kind, title: r.title ?? String(r.id), detail: [r.artwork_type_title, plain(r.artist_display), r.date_display].filter(Boolean).join(' · '), about: [r.medium_display, r.thumbnail?.alt_text].filter(Boolean).join(' · ').slice(0, 300), license: 'CC0 (public-domain artwork, Art Institute of Chicago)', licenseUrl: 'https://www.artic.edu/open-access/open-access-images', attribution: `Art Institute of Chicago (${r.id})`, pageUrl: `https://www.artic.edu/artworks/${r.id}`, imageUrl: `${base}/${width && width < 1686 ? width : 1686},/0/default.jpg`, previewUrl: `${base}/400,/0/default.jpg`, originalWidth: width, originalHeight: height }];
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
async function searchCleveland(query, limit, opts, kind = 'design') {
|
|
354
|
+
const data = await json(apiUrl('https://openaccess-api.clevelandart.org/api/artworks/', { q: query, has_image: 1, cc0: 1, limit: Math.min(limit * 2, 40) }), opts);
|
|
355
|
+
return (data.data ?? []).flatMap(r => {
|
|
356
|
+
const web = r.images?.web, print = r.images?.print, full = r.images?.full;
|
|
357
|
+
if (!web?.url) return [];
|
|
358
|
+
return [{ provider: 'cleveland', sourceId: String(r.id), kind, title: r.title ?? String(r.id), detail: [r.type, ...(r.creators ?? []).map(c => c.description), r.creation_date].filter(Boolean).join(' · '), about: plain(r.description ?? r.technique ?? '').slice(0, 300), license: 'CC0', licenseUrl: 'https://www.clevelandart.org/open-access', attribution: r.creditline ? `Cleveland Museum of Art · ${r.creditline}` : 'Cleveland Museum of Art', pageUrl: r.url, imageUrl: https((print ?? web).url), previewUrl: https(web.url), originalWidth: Number(full?.width ?? print?.width) || undefined, originalHeight: Number(full?.height ?? print?.height) || undefined }];
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
async function searchMet(query, limit, opts, kind = 'design') {
|
|
362
|
+
// v1 search was retired on 2026-10-01; v1.1 pages results, and each object needs its own request.
|
|
363
|
+
const data = await json(apiUrl('https://collectionapi.metmuseum.org/public/collection/v1.1/search', { q: query, hasImages: 'true', isPublicDomain: 'true', limit: Math.min(limit * 2, 12) }), opts);
|
|
364
|
+
const out = await pool(data.objectIDs ?? [], id => json(`https://collectionapi.metmuseum.org/public/collection/v1/objects/${id}`, opts), 4);
|
|
365
|
+
return out.flatMap(r => {
|
|
366
|
+
const o = r.status === 'fulfilled' ? r.value : null;
|
|
367
|
+
if (!o?.isPublicDomain || !o.primaryImage) return [];
|
|
368
|
+
return [{ provider: 'met', sourceId: String(o.objectID), kind, title: o.title || o.objectName || String(o.objectID), detail: [o.objectName, o.artistDisplayName, o.objectDate, o.culture].filter(Boolean).join(' · '), about: [o.medium, o.classification].filter(Boolean).join(' · ').slice(0, 300), license: 'CC0 (Met Open Access, public domain)', licenseUrl: 'https://www.metmuseum.org/policies/image-resources', attribution: `The Metropolitan Museum of Art (${o.objectID})`, pageUrl: o.objectURL, imageUrl: https(o.primaryImage), previewUrl: https(o.primaryImageSmall || o.primaryImage) }];
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
const JAPAN_RIGHTS = { cc0: 'CC0', pdm: 'Public Domain Mark', ccby: 'CC BY 4.0', ccbysa: 'CC BY-SA 4.0' };
|
|
372
|
+
async function searchJapan(query, limit, opts, kind = 'design') {
|
|
373
|
+
// Japan Search gathers ColBase (the national museums), the National Diet Library and regional collections; only openly licensed images are requested.
|
|
374
|
+
const u = new URL('https://jpsearch.go.jp/api/item/search/jps-cross');
|
|
375
|
+
u.searchParams.set('keyword', query); u.searchParams.set('f-contents', 'image'); u.searchParams.set('size', String(Math.min(limit * 3, 60)));
|
|
376
|
+
for (const r of Object.keys(JAPAN_RIGHTS)) u.searchParams.append('f-rights', '+' + r);
|
|
377
|
+
const data = await json(u.href, opts);
|
|
378
|
+
return (data.list ?? []).flatMap(x => {
|
|
379
|
+
const c = x.common ?? {}, raw = c.contentsUrl?.[0] ?? c.thumbnailUrl?.[0];
|
|
380
|
+
if (!raw || !JAPAN_RIGHTS[c.contentsRightsType]) return [];
|
|
381
|
+
// ColBase keeps a 3000px image at /slideshow/ beside the small /slideshow_s/; some items lack it, so the small one stays as fallback.
|
|
382
|
+
const original = https(raw), large = original.replace('/slideshow_s/', '/slideshow/');
|
|
383
|
+
const people = (c.contributor ?? []).filter(Boolean).slice(0, 2).join(' / ');
|
|
384
|
+
return [{ provider: 'japan', sourceId: c.id ?? x.id, kind, title: plain(c.title) || c.id, detail: [plain(c.titleEn), (c.temporal ?? []).at(-1), people].filter(Boolean).join(' · ').slice(0, 240), about: plain(c.description).slice(0, 300), license: JAPAN_RIGHTS[c.contentsRightsType], licenseUrl: 'https://jpsearch.go.jp/policy/available-rights-statements', attribution: c.database === 'cobas' ? `出典:ColBase(https://colbase.nich.go.jp/) · ${people}` : [people || c.provider, 'via Japan Search'].filter(Boolean).join(' · '), pageUrl: c.linkUrl ?? `https://jpsearch.go.jp/item/${c.id}`, imageUrl: large, previewUrl: https(c.thumbnailUrl?.[0] ?? raw), downloadCandidates: [...new Set([large, original])] }];
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
async function searchRijks(query, limit, opts, kind = 'design') {
|
|
388
|
+
// Linked Art: the search lists object ids; the image sits two hops away (object → visual item → digital object).
|
|
389
|
+
const data = await json(apiUrl('https://data.rijksmuseum.nl/search/collection', { description: query, imageAvailable: 'true' }), opts);
|
|
390
|
+
const out = await pool((data.orderedItems ?? []).slice(0, Math.min(limit * 2, 10)).map(x => x.id), async id => {
|
|
391
|
+
const o = await json(id, opts);
|
|
392
|
+
const visual = await json(o.shows[0].id, opts), digital = await json(visual.digitally_shown_by[0].id, opts);
|
|
393
|
+
const service = digital.access_point?.[0]?.id?.replace(/\/full\/.*$/, '');
|
|
394
|
+
if (!service) return null;
|
|
395
|
+
const names = (o.identified_by ?? []).filter(n => n.type === 'Name'), english = names.find(n => JSON.stringify(n.language ?? '').includes('300388277'));
|
|
396
|
+
const rights = JSON.stringify(o.subject_of ?? '').match(/https?:\/\/creativecommons\.org\/[a-z/]+\/[\d.]+\//)?.[0];
|
|
397
|
+
const number = (o.identified_by ?? []).find(n => n.type === 'Identifier')?.content;
|
|
398
|
+
return { provider: 'rijks', sourceId: id.split('/').pop(), kind, title: (english ?? names[0])?.content ?? number ?? id, detail: [names.find(n => n !== english)?.content, number].filter(Boolean).join(' · ').slice(0, 240), about: (o.referred_to_by ?? []).map(r => r.content).filter(Boolean).join(' · ').slice(0, 300), license: rights ? (rights.includes('/zero/') ? 'CC0' : rights.includes('/mark/') ? 'Public Domain Mark' : rights) : 'Rights not specified in record; check item page', licenseUrl: rights ?? id, attribution: `Rijksmuseum, Amsterdam${number ? ` (${number})` : ''}`, pageUrl: id, imageUrl: `${service}/full/!2400,2400/0/default.jpg`, previewUrl: `${service}/full/!500,500/0/default.jpg` };
|
|
399
|
+
}, 4);
|
|
400
|
+
return out.flatMap(r => r.status === 'fulfilled' && r.value ? [r.value] : []);
|
|
401
|
+
}
|
|
402
|
+
export function interleave(groups, limit) {
|
|
403
|
+
const out = [], seen = new Set();
|
|
404
|
+
for (let i = 0; groups.some(g => i < g.length) && out.length < limit; i++) {
|
|
405
|
+
for (const group of groups) {
|
|
406
|
+
const item = group[i]; if (!item) continue;
|
|
407
|
+
// Archives often list one work as several records; identical title and detail add nothing to compare.
|
|
408
|
+
const key = `${item.provider}:${item.sourceId}`, twin = `${item.provider}:${item.title}:${item.detail ?? ''}`.toLowerCase();
|
|
409
|
+
if (seen.has(key) || seen.has(twin)) continue; seen.add(key); seen.add(twin); out.push(item);
|
|
410
|
+
if (out.length === limit) break;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
return out;
|
|
414
|
+
}
|
|
415
|
+
async function remember(items, opts) {
|
|
416
|
+
return updateCache(opts.cache, cache => items.map(item => {
|
|
417
|
+
const existing = Object.values(cache.items).find(x => x.provider === item.provider && x.sourceId === item.sourceId);
|
|
418
|
+
const prefix = PREFIX[item.provider];
|
|
419
|
+
const next = Math.max(0, ...Object.keys(cache.items).filter(k => k.startsWith(prefix)).map(k => Number(k.slice(1)) || 0)) + 1;
|
|
420
|
+
const ref = existing?.ref ?? prefix + next;
|
|
421
|
+
const result = { ...existing, ...item, ref, cachedAt: new Date().toISOString() }; cache.items[ref] = result; return result;
|
|
422
|
+
}));
|
|
423
|
+
}
|
|
424
|
+
function searchJobs(query, opts) {
|
|
425
|
+
const kinds = opts.kind === 'all' ? 15 : 1;
|
|
426
|
+
const per = Math.max(2, Math.ceil(opts.limit / kinds));
|
|
427
|
+
const jobs = {
|
|
428
|
+
texture: [['polyhaven/textures', () => searchPoly(query, per, opts)]],
|
|
429
|
+
design: [['met', () => searchMet(query, per, opts)], ['aic', () => searchAic(query, per, opts)], ['cleveland', () => searchCleveland(query, per, opts)], ['vam', () => searchVam(query, per, opts)], ['polyhaven/models', () => searchPoly(query, per, opts, 'models')], ['japan', () => searchJapan(query, per, opts)], ['rijks', () => searchRijks(query, per, opts)]],
|
|
430
|
+
photo: [['commons', () => searchCommons(query, per, opts)], ['loc/photos', () => searchLoc(query, per, opts)], ['nasa/images', () => searchNasa(query, per, opts, 'image', 'photo')]],
|
|
431
|
+
blueprint: [['loc/hh', () => searchLoc(query, per, opts, 'blueprint')]],
|
|
432
|
+
map: [['loc/maps', () => searchLoc(query, per, opts, 'map')], ['commons/maps', () => searchCommons(`${query} (map OR chart OR plan)`, per, opts, 'map')]],
|
|
433
|
+
plate: [['wellcome', () => searchWellcome(query, per, opts)], ['japan/plates', () => searchJapan(query, per, opts, 'plate')], ['commons/plates', () => searchCommons(`${query} engraving`, per, opts, 'plate')]],
|
|
434
|
+
science: [['nasa/science', () => searchNasa(query, per, opts)], ['wellcome/science', () => searchWellcome(query, per, opts, 'science')]],
|
|
435
|
+
video: [['nasa/video', () => searchNasa(query, per, opts, 'video', 'video')]],
|
|
436
|
+
};
|
|
437
|
+
if (opts.kind !== 'all') return jobs[opts.kind];
|
|
438
|
+
// One job per source, so a single slow or empty source cannot take the whole first round.
|
|
439
|
+
return [jobs.photo[0], jobs.design[5], jobs.texture[0], jobs.design[1], jobs.photo[1], jobs.design[6], jobs.design[2], jobs.plate[0], jobs.design[0], jobs.science[0], jobs.blueprint[0], jobs.map[0], jobs.design[3], jobs.video[0], jobs.design[4]];
|
|
440
|
+
}
|
|
441
|
+
async function runSearch(input, opts) {
|
|
442
|
+
const queries = input.split(/\s*\|\s*/).map(q => q.trim()).filter(Boolean);
|
|
443
|
+
if (!queries.length) usage('search 缺少关键词');
|
|
444
|
+
const selected = queries.flatMap(query => searchJobs(query, opts).map(([label, fn]) => [queries.length > 1 ? `${label} "${query}"` : label, fn, query]));
|
|
445
|
+
let filtered = 0;
|
|
446
|
+
const results = await Promise.allSettled(selected.map(async ([label, fn, query]) => {
|
|
447
|
+
const start = Date.now(); const value = await fn(); let items = Array.isArray(value) ? value : value.items;
|
|
448
|
+
const terms = queryTerms(query), before = items.length;
|
|
449
|
+
// Sources that search full records return items whose title and summary never mention the subject.
|
|
450
|
+
// full: the item names every query term. Full matches are picked across sources first, so a medium word alone ("engraving") cannot crowd out the subject.
|
|
451
|
+
// CJK queries reach records catalogued only in English, so a CJK miss ranks lower instead of being dropped.
|
|
452
|
+
items = items.map(item => { const score = item.matched ?? matchCount(`${item.title} ${item.detail} ${item.about ?? ''}`, terms); return { ...item, score, full: score >= terms.length }; });
|
|
453
|
+
if (terms.length && terms.every(t => /^[\p{Script=Latin}\p{N}]+$/u.test(t))) items = items.filter(item => item.score > 0);
|
|
454
|
+
filtered += before - items.length;
|
|
455
|
+
items = items.map(({ matched, score, ...item }) => ({ ...item, use: item.use ?? rightsUse(item.license, item.licenseUrl), query }));
|
|
456
|
+
if (opts.use === 'page') items = items.filter(item => item.use === 'page');
|
|
457
|
+
console.error(`[curate-media] ${label}: ${items.length} 项${before > items.length ? `(滤掉 ${before - items.length})` : ''} (${Date.now() - start}ms)`);
|
|
458
|
+
return { items, errors: value.errors ?? [] };
|
|
459
|
+
}));
|
|
460
|
+
const errors = results.flatMap((r, i) => r.status === 'rejected' ? [{ source: selected[i][0], message: r.reason.message }] : r.value.errors.map(message => ({ source: selected[i][0], message })));
|
|
461
|
+
const groups = results.filter(r => r.status === 'fulfilled').map(r => r.value.items);
|
|
462
|
+
const full = interleave(groups.map(g => g.filter(i => i.full)), opts.limit), taken = new Set(full.map(i => `${i.provider}:${i.sourceId}`));
|
|
463
|
+
const partial = interleave(groups.map(g => g.filter(i => !i.full && !taken.has(`${i.provider}:${i.sourceId}`))), opts.limit - full.length);
|
|
464
|
+
const picked = [...full, ...partial].map(({ full, ...item }) => item).sort((a, b) => USES[a.use] - USES[b.use]);
|
|
465
|
+
let items = await remember(picked, opts);
|
|
466
|
+
let sheet;
|
|
467
|
+
if (opts.sheet) ({ items, sheet } = await remoteSheet(items, opts));
|
|
468
|
+
const hint = items.length ? undefined : '零结果:每组只留一两个词,先搜地名或物名本身,或用 | 分开几种说法;--kind 换成 all';
|
|
469
|
+
return { query: input, ...(queries.length > 1 ? { queries } : {}), kind: opts.kind, count: items.length, ...(hint ? { hint } : {}), filtered, cache: opts.cache, items, errors, ...sheet, failed: results.every(r => r.status === 'rejected') || (!items.length && errors.length > 0) };
|
|
470
|
+
}
|
|
471
|
+
async function targets(positional, opts) {
|
|
472
|
+
const tokens = positional.flatMap(x => /^https?:\/\//i.test(x) ? [x] : x.split(/[,\s]+/)).filter(Boolean);
|
|
473
|
+
const cache = await readCache(opts.cache);
|
|
474
|
+
for (const extra of [path.join(opts.dir, '.curate-cache.json'), path.join(path.dirname(opts.dir), '.curate-cache.json')]) {
|
|
475
|
+
if (extra !== opts.cache && existsSync(extra)) {
|
|
476
|
+
try { const other = await readCache(extra); cache.items = { ...other.items, ...cache.items }; } catch {}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
return [...new Set(tokens)].map(token => {
|
|
480
|
+
if (/^[A-Z]\d+$/i.test(token) && Object.values(PREFIX).includes(token[0].toUpperCase())) {
|
|
481
|
+
const item = cache.items[token.toUpperCase()];
|
|
482
|
+
if (!item) usage(`缓存中没有 ${token};先 search,或用 --cache 选择原缓存`);
|
|
483
|
+
return item;
|
|
484
|
+
}
|
|
485
|
+
try {
|
|
486
|
+
const url = httpUrl(token);
|
|
487
|
+
// A pasted result URL retains its source metadata whenever the workspace knows it.
|
|
488
|
+
const known = Object.values(cache.items).find(x => [x.imageUrl, x.videoUrl, ...Object.values(x.textureMaps ?? {}).map(m => m?.url)].includes(url));
|
|
489
|
+
return { ...known, directUrl: url, imageUrl: url, title: known?.title ?? path.basename(new URL(url).pathname), provider: known?.provider ?? 'url', use: known?.use ?? 'check', license: known?.license ?? 'Rights not verified (direct URL)', attribution: known?.attribution ?? 'Source supplied by user', pageUrl: known?.pageUrl ?? url, previewUrl: url };
|
|
490
|
+
} catch { usage(`无效编号或 URL:${token}`); }
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
const escapeHtml = s => String(s ?? '').replace(/[&<>"']/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]));
|
|
494
|
+
async function writeSheet(items, dir, remote) {
|
|
495
|
+
await fs.mkdir(dir, { recursive: true });
|
|
496
|
+
const cards = items.map(item => {
|
|
497
|
+
const src = remote ? item.previewUrl ?? item.imageUrl : item.poster ?? item.saved;
|
|
498
|
+
const href = remote ? src : src && path.relative(dir, src).split(path.sep).map(encodeURIComponent).join('/');
|
|
499
|
+
const image = href && !/\.(mp4|mov|webm)(?:\?|$)/i.test(href) ? `<img src="${escapeHtml(href)}" alt="${escapeHtml(item.title)}"><p class="size">加载中…</p>` : '<p>没有图片预览;请查看下载结果</p>';
|
|
500
|
+
return `<article>${image}<h2>${escapeHtml(item.ref ?? 'URL')} · ${escapeHtml(item.use ?? 'check')} · ${escapeHtml(item.map ?? item.kind)} · ${escapeHtml(item.title)}</h2><p>${escapeHtml(item.license)}</p><p>${escapeHtml(item.attribution)}</p><a href="${escapeHtml(item.pageUrl)}">来源记录</a>${item.integrity ? `<p>${escapeHtml(item.integrity)} · ${item.width ?? '?'} × ${item.height ?? '?'}</p>` : ''}</article>`;
|
|
501
|
+
}).join('\n');
|
|
502
|
+
const file = path.join(dir, remote ? 'candidates.html' : 'sheet.html');
|
|
503
|
+
const html = `<!doctype html><html lang="zh-CN"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>素材候选联系表</title><style>body{font:15px/1.5 system-ui;margin:24px;background:#eee;color:#222}main{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:20px}article{background:white;padding:16px;overflow-wrap:anywhere}img{width:100%;height:240px;object-fit:contain}h2{font-size:17px;line-height:1.4}.error{color:#a00}</style><h1>素材候选 · ${items.length} 项</h1><p>${remote ? '远程预览:宽高为预览文件尺寸,原图尺寸在下载结果中。' : '本地素材:解码结果与真实宽高见各项说明。'}</p><main>${cards}</main><script>for(const img of document.images){const label=img.nextElementSibling;function check(){label.textContent=img.naturalWidth?img.naturalWidth+' × '+img.naturalHeight:'加载失败';if(!img.naturalWidth)label.className='error'}img.addEventListener('load',check);img.addEventListener('error',check);if(img.complete)check()}</script></html>`;
|
|
504
|
+
await fs.writeFile(file, html); return file;
|
|
505
|
+
}
|
|
506
|
+
async function hasTool(name) { try { await exec(name, ['-version'], { timeout: 5000, maxBuffer: 100000 }); return true; } catch { return false; } }
|
|
507
|
+
// Design facts from a 48×48 thumbnail: where text can sit, what the edges fuse with, whether ink sits on paper.
|
|
508
|
+
// Calm needs both low spread and low neighbour contrast; spread alone calls a fine-lined map calm. Thresholds come from real previews.
|
|
509
|
+
export function describePixels(buf, n = 48) {
|
|
510
|
+
const px = i => [buf[i * 3], buf[i * 3 + 1], buf[i * 3 + 2]];
|
|
511
|
+
const lum = i => { const [r, g, b] = px(i); return (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255; };
|
|
512
|
+
const region = (x0, y0, x1, y1) => {
|
|
513
|
+
let sum = 0, sq = 0, sat = 0, grad = 0, pairs = 0; const rgb = [0, 0, 0], count = (x1 - x0) * (y1 - y0);
|
|
514
|
+
for (let y = y0; y < y1; y++) for (let x = x0; x < x1; x++) {
|
|
515
|
+
const i = y * n + x, v = lum(i), c = px(i), max = Math.max(...c);
|
|
516
|
+
sum += v; sq += v * v; sat += max ? (max - Math.min(...c)) / max : 0; c.forEach((value, k) => { rgb[k] += value; });
|
|
517
|
+
if (x + 1 < x1) { grad += Math.abs(v - lum(i + 1)); pairs++; }
|
|
518
|
+
if (y + 1 < y1) { grad += Math.abs(v - lum(i + n)); pairs++; }
|
|
519
|
+
}
|
|
520
|
+
const mean = sum / count;
|
|
521
|
+
return { mean, sd: Math.sqrt(Math.max(0, sq / count - mean * mean)), sat: sat / count, grad: grad / pairs, rgb: rgb.map(v => v / count) };
|
|
522
|
+
};
|
|
523
|
+
const calmOf = r => r.sd < 0.09 && r.grad < 0.02;
|
|
524
|
+
const all = region(0, 0, n, n), t = Math.round(n / 3), e = 3;
|
|
525
|
+
const strips = [region(0, 0, n, e), region(0, n - e, n, n), region(0, 0, e, n), region(n - e, 0, n, n)];
|
|
526
|
+
const edgeFlat = strips.every(r => r.sd < 0.06 && r.grad < 0.02);
|
|
527
|
+
const ring = { mean: strips.reduce((v, r) => v + r.mean, 0) / 4, sat: strips.reduce((v, r) => v + r.sat, 0) / 4, rgb: [0, 1, 2].map(k => strips.reduce((v, r) => v + r.rgb[k], 0) / 4) };
|
|
528
|
+
const regions = { top: region(0, 0, n, t), bottom: region(0, n - t, n, n), left: region(0, 0, t, n), right: region(n - t, 0, n, n), centre: region(t, t, n - t, n - t) };
|
|
529
|
+
const hex = rgb => '#' + rgb.map(v => Math.round(v).toString(16).padStart(2, '0')).join('');
|
|
530
|
+
return { tone: all.mean < 0.33 ? 'dark' : all.mean > 0.66 ? 'light' : 'mid', mono: all.sat < 0.1, edge: hex(ring.rgb), edgeFlat, paper: edgeFlat && ring.mean > 0.75 && ring.sat < 0.2, calm: Object.keys(regions).filter(k => calmOf(regions[k])) };
|
|
531
|
+
}
|
|
532
|
+
async function lookAt(file, info) {
|
|
533
|
+
const { stdout } = await exec('ffmpeg', ['-nostdin', '-v', 'error', '-i', file, '-frames:v', '1', '-vf', 'scale=48:48:flags=area,format=rgb24', '-f', 'rawvideo', 'pipe:1'], { encoding: 'buffer', timeout: 20000, maxBuffer: 100000 });
|
|
534
|
+
if (stdout.length < 48 * 48 * 3) throw new Error('预览无法解码');
|
|
535
|
+
return { ...describePixels(stdout), ratio: +(info.width / info.height).toFixed(2) };
|
|
536
|
+
}
|
|
537
|
+
const FONTS = ['/System/Library/Fonts/Supplemental/Arial.ttf', '/System/Library/Fonts/Helvetica.ttc', '/Library/Fonts/Arial.ttf', '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', '/usr/share/fonts/dejavu/DejaVuSans.ttf', '/usr/share/fonts/TTF/DejaVuSans.ttf', '/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf', 'C:/Windows/Fonts/arial.ttf', 'C:/Windows/Fonts/segoeui.ttf'];
|
|
538
|
+
const labelText = s => String(s ?? '').normalize('NFKD').replace(/[^A-Za-z0-9 ./#+()-]/g, ' ').replace(/\s+/g, ' ').trim().slice(0, 46);
|
|
539
|
+
// One PNG an agent can read in a single look: each cell carries its ref, use and size.
|
|
540
|
+
async function contactSheet(cells, file) {
|
|
541
|
+
const cols = Math.min(4, cells.length), rows = Math.ceil(cells.length / cols), W = 360, H = 300, IW = W - 16, IH = H - 62;
|
|
542
|
+
const graph = [`color=c=0xEDEDED:s=${cols * W}x${rows * H}:d=1[b0]`];
|
|
543
|
+
cells.forEach((_, i) => {
|
|
544
|
+
const x = (i % cols) * W + 8, y = Math.floor(i / cols) * H + 8;
|
|
545
|
+
graph.push(`[${i}:v]scale=${IW}:${IH}:force_original_aspect_ratio=decrease[s${i}]`, `[b${i}][s${i}]overlay=x='${x}+(${IW}-w)/2':y='${y}+(${IH}-h)/2'[b${i + 1}]`);
|
|
546
|
+
});
|
|
547
|
+
const font = FONTS.find(f => existsSync(f));
|
|
548
|
+
const draw = (t, x, y, size, color) => `drawtext=${font ? `fontfile='${font.replace(/:/g, '\\:')}':` : ''}text='${labelText(t)}':x=${x}:y=${y}:fontsize=${size}:fontcolor=${color}`;
|
|
549
|
+
const labels = cells.flatMap((c, i) => { const x = (i % cols) * W + 10, y = Math.floor(i / cols) * H + H - 50; return [draw(c.top, x, y, 18, '0x111111'), draw(c.bottom, x, y + 25, 13, '0x555555')]; });
|
|
550
|
+
const run = withLabels => exec('ffmpeg', ['-nostdin', '-v', 'error', '-y', ...cells.flatMap(c => ['-i', c.src]), '-filter_complex', graph.join(';') + (withLabels ? `;[b${cells.length}]${labels.join(',')}[out]` : ''), '-map', withLabels ? '[out]' : `[b${cells.length}]`, '-frames:v', '1', '-update', '1', file], { timeout: 60000, maxBuffer: 1000000 });
|
|
551
|
+
try { await run(true); return { file, labelled: true }; }
|
|
552
|
+
catch { await run(false); return { file, labelled: false }; }
|
|
553
|
+
}
|
|
554
|
+
const sizeLabel = item => item.originalWidth && item.originalHeight ? `${item.originalWidth}x${item.originalHeight}` : item.width && item.height ? `${item.width}x${item.height}` : '';
|
|
555
|
+
async function remoteSheet(items, opts) {
|
|
556
|
+
const html = await writeSheet(items, opts.dir, true);
|
|
557
|
+
if (!(await hasTool('ffmpeg')) || !(await hasTool('ffprobe'))) return { items, sheet: { sheet: html, sheetWarnings: ['缺少 ffmpeg/ffprobe,没有联系表 PNG 和 look;打开 HTML 联系表挑选'] } };
|
|
558
|
+
const temp = await fs.mkdtemp(path.join(os.tmpdir(), 'oil-curate-'));
|
|
559
|
+
try {
|
|
560
|
+
const looked = await pool(items, async (item, i) => {
|
|
561
|
+
const file = path.join(temp, `${i}.part`);
|
|
562
|
+
await download(item.previewUrl ?? item.imageUrl, file, { ...opts, 'download-timeout': opts.timeout });
|
|
563
|
+
const size = await inspect(file, { ffprobe: true }, false);
|
|
564
|
+
return { file, size, look: await lookAt(file, size) };
|
|
565
|
+
}, 6);
|
|
566
|
+
const out = items.map((item, i) => looked[i].status !== 'fulfilled' ? { ...item, look: null, lookError: looked[i].reason.message } : { ...item, look: looked[i].value.look, ...(!item.originalWidth && item.previewUrl === item.imageUrl ? { originalWidth: looked[i].value.size.width, originalHeight: looked[i].value.size.height } : {}) });
|
|
567
|
+
const cells = out.flatMap((item, i) => looked[i].status === 'fulfilled' ? [{ src: looked[i].value.file, top: `${item.ref ?? 'URL'} ${item.use ?? ''} ${sizeLabel(item)}`, bottom: `${item.provider} - ${/[a-z]{3}/i.test(labelText(item.title)) ? item.title : item.detail}` }] : []);
|
|
568
|
+
const warnings = out.filter(x => x.lookError).map(x => `${x.ref ?? x.imageUrl}: 预览下载失败,未进 PNG:${x.lookError}`);
|
|
569
|
+
if (!cells.length) return { items: out, sheet: { sheet: html, sheetWarnings: warnings } };
|
|
570
|
+
const png = await contactSheet(cells, path.join(opts.dir, 'candidates.png'));
|
|
571
|
+
if (!png.labelled) warnings.push('ffmpeg 无法写字,PNG 没有编号;格子按 items 中有 look 的条目从左到右、从上到下排列');
|
|
572
|
+
console.error(`[curate-media] 联系表图片:${png.file}(直接读这张图挑选)`);
|
|
573
|
+
return { items: out, sheet: { sheet: html, sheetImage: png.file, ...(warnings.length ? { sheetWarnings: warnings } : {}) } };
|
|
574
|
+
} finally { await fs.rm(temp, { recursive: true, force: true }); }
|
|
575
|
+
}
|
|
576
|
+
export function sniff(buffer) {
|
|
577
|
+
if (buffer[0] === 0xff && buffer[1] === 0xd8 && buffer[2] === 0xff) return { ext: '.jpg', video: false };
|
|
578
|
+
if (buffer.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]))) return { ext: '.png', video: false };
|
|
579
|
+
if (buffer.toString('ascii', 0, 6).match(/^GIF8[79]a$/)) return { ext: '.gif', video: false };
|
|
580
|
+
if (buffer.toString('ascii', 0, 4) === 'RIFF' && buffer.toString('ascii', 8, 12) === 'WEBP') return { ext: '.webp', video: false };
|
|
581
|
+
if (buffer.toString('ascii', 4, 8) === 'ftyp') return { ext: buffer.toString('ascii', 8, 12).startsWith('qt') ? '.mov' : '.mp4', video: true };
|
|
582
|
+
if (buffer.subarray(0, 4).equals(Buffer.from([0x1a, 0x45, 0xdf, 0xa3]))) return { ext: '.webm', video: true };
|
|
583
|
+
throw new Error('响应不是支持的图片或视频;可能是 HTML、JSON 错误页或损坏的文件');
|
|
584
|
+
}
|
|
585
|
+
async function download(url, temp, opts, expected) {
|
|
586
|
+
const res = await fetch(httpUrl(url), { headers: headersFor(url), signal: AbortSignal.timeout(opts['download-timeout']) });
|
|
587
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}: ${url}`);
|
|
588
|
+
const max = opts['max-mb'] * 1024 * 1024, length = Number(res.headers.get('content-length'));
|
|
589
|
+
if (length > max) { await res.body?.cancel(); throw new Error(`文件超过 --max-mb ${opts['max-mb']}`); }
|
|
590
|
+
if (/text\/html|application\/json/.test(res.headers.get('content-type') ?? '')) { await res.body?.cancel(); throw new Error('响应是错误页或元数据,不是媒体文件'); }
|
|
591
|
+
const handle = await fs.open(temp, 'wx'); let bytes = 0, head = Buffer.alloc(0); const md5 = createHash('md5');
|
|
592
|
+
try {
|
|
593
|
+
for await (const chunk of res.body) {
|
|
594
|
+
bytes += chunk.length; if (bytes > max) throw new Error(`文件超过 --max-mb ${opts['max-mb']}`);
|
|
595
|
+
if (head.length < 32) head = Buffer.concat([head, Buffer.from(chunk)]).subarray(0, 32);
|
|
596
|
+
md5.update(chunk); await handle.writeFile(chunk);
|
|
597
|
+
}
|
|
598
|
+
} finally { await handle.close(); }
|
|
599
|
+
if (!bytes || (length && !res.headers.get('content-encoding') && bytes !== length)) throw new Error('文件为空或传输不完整');
|
|
600
|
+
if (expected?.size && bytes !== expected.size) throw new Error(`文件大小不符:${bytes},预期 ${expected.size}`);
|
|
601
|
+
const digest = md5.digest('hex'); if (expected?.md5 && digest !== expected.md5) throw new Error('MD5 校验失败');
|
|
602
|
+
return { bytes, md5: digest, ...sniff(head) };
|
|
603
|
+
}
|
|
604
|
+
async function inspect(file, tools, full) {
|
|
605
|
+
if (!tools.ffprobe) return { integrity: 'signature-and-length-only', warnings: ['缺少 ffprobe,尚未检查真实宽高'] };
|
|
606
|
+
const { stdout } = await exec('ffprobe', ['-v', 'error', '-select_streams', 'v:0', '-show_entries', 'stream=width,height,codec_name:format=duration', '-of', 'json', file], { timeout: 20000, maxBuffer: 100000 });
|
|
607
|
+
const data = JSON.parse(stdout), stream = data.streams?.[0];
|
|
608
|
+
if (!stream?.width || !stream?.height) throw new Error('没有可解码的图像/视频流');
|
|
609
|
+
if (full && tools.ffmpeg) await exec('ffmpeg', ['-nostdin', '-v', 'error', '-xerror', '-i', file, '-map', '0:v:0', '-f', 'null', '-'], { timeout: 120000, maxBuffer: 1000000 });
|
|
610
|
+
return { width: stream.width, height: stream.height, codec: stream.codec_name, duration: Number(data.format?.duration) || undefined, integrity: full && tools.ffmpeg ? 'full-decode' : 'probed', warnings: full && !tools.ffmpeg ? ['缺少 ffmpeg,尚未完整解码'] : [] };
|
|
611
|
+
}
|
|
612
|
+
const safeName = s => String(s ?? 'media').replace(/[^a-zA-Z0-9_-]+/g, '-').slice(0, 90) || 'media';
|
|
613
|
+
// Reserve the destination atomically; repeated downloads get suffixes instead of overwriting.
|
|
614
|
+
async function deliver(temp, wanted, explicit = false) {
|
|
615
|
+
for (let i = 0; i < 1000; i++) {
|
|
616
|
+
const p = path.parse(wanted), file = i ? path.join(p.dir, `${p.name}-${i}${p.ext}`) : wanted;
|
|
617
|
+
try { await fs.copyFile(temp, file, fs.constants.COPYFILE_EXCL); return file; } catch (e) { if (e.code !== 'EEXIST') throw e; if (explicit) throw new Error(`文件已存在:${file};请换一个 --out 文件名`); }
|
|
618
|
+
}
|
|
619
|
+
throw new Error('文件名冲突过多');
|
|
620
|
+
}
|
|
621
|
+
const mdText = s => String(s ?? '').replace(/[\r\n]+/g, ' ').replace(/[`\[\]<>]/g, '');
|
|
622
|
+
async function logSources(records, note) {
|
|
623
|
+
const groups = new Map();
|
|
624
|
+
for (const r of records) {
|
|
625
|
+
const dir = path.dirname(r.saved); if (!groups.has(dir)) groups.set(dir, []);
|
|
626
|
+
const line = `- \`${path.basename(r.saved)}\` — ${mdText(r.ref ?? r.provider)} · ${mdText(r.title)}${r.map ? ' · ' + r.map : ''}\n - Use: ${r.use === 'reference' ? 'reference only, not for the page' : r.use === 'page' ? 'page' : 'check rights on the source page'}\n - Source: ${r.pageUrl}\n - Download: ${r.url}\n - License: ${mdText(r.license)} (${r.licenseUrl ?? r.pageUrl})\n - Credit: ${mdText(r.attribution)}${note ? '\n - Note: ' + mdText(note) : ''}\n`;
|
|
627
|
+
groups.get(dir).push(line);
|
|
628
|
+
if (r.poster) groups.get(dir).push(`- \`${path.basename(r.poster)}\` — first-frame poster derived from \`${path.basename(r.saved)}\`; same license and credit as above.\n`);
|
|
629
|
+
}
|
|
630
|
+
for (const [dir, lines] of groups) await fs.appendFile(path.join(dir, 'SOURCES.md'), lines.join('\n') + '\n');
|
|
631
|
+
}
|
|
632
|
+
async function resolveIIIF(item, opts) {
|
|
633
|
+
const info = await json(item.iiifInfoUrl, opts);
|
|
634
|
+
if (!info.width || !info.height) throw new Error('IIIF 没有有效的原图尺寸');
|
|
635
|
+
const base = iiifBase(item.iiifInfoUrl);
|
|
636
|
+
const imageUrl = Math.max(info.width, info.height) <= 1600 ? `${base}/full/full/0/default.jpg` : `${base}/full/!1600,1600/0/default.jpg`;
|
|
637
|
+
return { ...item, imageUrl, originalWidth: info.width, originalHeight: info.height };
|
|
638
|
+
}
|
|
639
|
+
// Final picks get the same look fields and a PNG, measured on the files the page will use.
|
|
640
|
+
async function localSheet(records, dir) {
|
|
641
|
+
const cells = [];
|
|
642
|
+
for (const r of records) {
|
|
643
|
+
const src = r.poster ?? (r.video ? null : r.saved); if (!src) continue;
|
|
644
|
+
try { r.look = await lookAt(src, await inspect(src, { ffprobe: true }, false)); cells.push({ src, top: `${r.ref ?? 'URL'} ${r.use} ${r.width && r.height ? `${r.width}x${r.height}` : ''}`, bottom: path.basename(r.saved) }); }
|
|
645
|
+
catch (e) { r.warnings.push(`look 失败:${e.message}`); }
|
|
646
|
+
}
|
|
647
|
+
return cells.length ? (await contactSheet(cells, path.join(dir, 'sheet.png'))).file : undefined;
|
|
648
|
+
}
|
|
649
|
+
async function runFetch(items, opts) {
|
|
650
|
+
if (opts.out && (items.length !== 1 || (items[0].kind === 'texture' && !items[0].directUrl && opts.maps.length > 1))) usage('--out 只支持单文件;批量拉取用 --dir assets');
|
|
651
|
+
await fs.mkdir(opts.dir, { recursive: true });
|
|
652
|
+
const tools = { ffmpeg: await hasTool('ffmpeg'), ffprobe: await hasTool('ffprobe') };
|
|
653
|
+
const tasks = [], errors = [], records = [];
|
|
654
|
+
for (let item of items) {
|
|
655
|
+
try {
|
|
656
|
+
if (!item.directUrl && item.kind === 'texture') item = await resolveTexture(item, opts);
|
|
657
|
+
else if (!item.directUrl && item.provider === 'nasa') item = await resolveNasa(item, opts);
|
|
658
|
+
else if (!item.directUrl && item.iiifInfoUrl) item = await resolveIIIF(item, opts);
|
|
659
|
+
if (item.kind === 'texture' && !item.directUrl) {
|
|
660
|
+
for (const map of opts.maps) {
|
|
661
|
+
const file = item.textureMaps[map];
|
|
662
|
+
if (!file) { errors.push({ ref: item.ref, map, message: `没有可用 ${map} JPG` }); continue; }
|
|
663
|
+
tasks.push({ item, map, url: file.url, expected: file });
|
|
664
|
+
}
|
|
665
|
+
} else tasks.push({ item, url: item.directUrl ?? item.videoUrl ?? item.imageUrl });
|
|
666
|
+
} catch (e) { errors.push({ ref: item.ref, message: e.message }); }
|
|
667
|
+
}
|
|
668
|
+
const settled = await pool(tasks, async ({ item, map, url, expected }) => {
|
|
669
|
+
const temp = path.join(opts.dir, `.${randomUUID()}.part`); let result;
|
|
670
|
+
try {
|
|
671
|
+
let received; const recovery = [];
|
|
672
|
+
const candidates = item.directUrl ? [url] : item.downloadCandidates ?? [url];
|
|
673
|
+
for (const candidate of candidates.slice(0, 3)) {
|
|
674
|
+
try { received = await download(candidate, temp, opts, expected); url = candidate; break; }
|
|
675
|
+
catch (e) { recovery.push(`${candidate}: ${e.message}`); await fs.rm(temp, { force: true }); }
|
|
676
|
+
}
|
|
677
|
+
if (!received) throw new Error(recovery.join('; '));
|
|
678
|
+
const inspection = await inspect(temp, tools, opts.sheet);
|
|
679
|
+
const filename = `${safeName(item.ref ?? item.provider)}-${safeName(item.sourceId ?? item.title)}${map ? '-' + map + '-' + expected.resolution : ''}${received.ext}`;
|
|
680
|
+
const out = opts.out ? path.resolve(opts.out) : path.join(opts.dir, filename);
|
|
681
|
+
if (opts.out && ![received.ext, ...(received.ext === '.jpg' ? ['.jpeg'] : [])].includes(path.extname(out).toLowerCase())) throw new Error(`--out 后缀与实际文件格式不一致,应为 ${received.ext}`);
|
|
682
|
+
const saved = await deliver(temp, out, Boolean(opts.out));
|
|
683
|
+
result = { ref: item.ref, provider: item.provider, sourceId: item.sourceId, kind: item.kind, use: item.use ?? rightsUse(item.license, item.licenseUrl), title: item.title, map, resolution: expected?.resolution, url, saved, pageUrl: item.pageUrl, license: item.license, licenseUrl: item.licenseUrl, attribution: item.attribution, originalWidth: item.originalWidth, originalHeight: item.originalHeight, ...(map === 'normal' ? { normalConvention: item.normalConvention } : {}), ...received, ...inspection };
|
|
684
|
+
if (!received.video && Math.max(result.width ?? 9999, result.height ?? 9999) < 1000) result.warnings.push(`尺寸偏小(${result.width}x${result.height}),只适合小图位`);
|
|
685
|
+
if (result.use === 'reference') result.warnings.push('仅作生图参考:授权不允许放进页面,放在不上线的目录');
|
|
686
|
+
if (recovery.length) result.warnings.push(`已回退到可用版本;之前失败:${recovery.join('; ')}`);
|
|
687
|
+
if (received.video) {
|
|
688
|
+
if (!tools.ffmpeg) result.warnings.push('缺少 ffmpeg,poster 未生成');
|
|
689
|
+
else {
|
|
690
|
+
const posterTemp = path.join(opts.dir, `.${randomUUID()}.jpg`);
|
|
691
|
+
try {
|
|
692
|
+
await exec('ffmpeg', ['-nostdin', '-v', 'error', '-xerror', '-i', saved, '-frames:v', '1', '-q:v', '2', posterTemp], { timeout: 30000, maxBuffer: 100000 });
|
|
693
|
+
result.poster = await deliver(posterTemp, saved.replace(/\.[^.]+$/, '-poster.jpg'));
|
|
694
|
+
} catch (e) { result.warnings.push(`poster 失败:${e.message}`); }
|
|
695
|
+
finally { await fs.rm(posterTemp, { force: true }); }
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
console.error(`[curate-media] ${item.ref ?? 'URL'}${map ? '/' + map : ''}: ${received.bytes} bytes → ${saved}`);
|
|
699
|
+
return result;
|
|
700
|
+
} finally { await fs.rm(temp, { force: true }); }
|
|
701
|
+
});
|
|
702
|
+
settled.forEach((r, i) => { if (r.status === 'fulfilled') records.push(r.value); else errors.push({ ref: tasks[i].item.ref, url: tasks[i].url, message: r.reason.message }); });
|
|
703
|
+
await logSources(records, opts.note);
|
|
704
|
+
for (const r of records) if ((opts.sheet && r.integrity !== 'full-decode') || (r.video && !r.poster)) errors.push({ ref: r.ref, saved: r.saved, message: r.warnings.join('; ') || '检查或 poster 未完成' });
|
|
705
|
+
const sheet = opts.sheet ? await writeSheet(records, opts.dir, false) : undefined;
|
|
706
|
+
const sheetImage = opts.sheet && tools.ffmpeg && tools.ffprobe ? await localSheet(records, opts.dir) : undefined;
|
|
707
|
+
return { count: records.length, files: records, errors, ...(sheet ? { sheet } : {}), ...(sheetImage ? { sheetImage } : {}), ...(records.length === 1 ? { saved: records[0].saved, bytes: records[0].bytes } : {}), failed: errors.length > 0 };
|
|
708
|
+
}
|
|
709
|
+
export async function main(argv = process.argv.slice(2)) {
|
|
710
|
+
const parsed = parseArgs(argv); if (parsed.help) { console.log(HELP); return; }
|
|
711
|
+
const { command, positional, opts } = parsed; const start = Date.now(); let result;
|
|
712
|
+
if (command === 'search') result = await runSearch(positional.join(' ').trim(), opts);
|
|
713
|
+
else {
|
|
714
|
+
const items = await targets(positional, opts);
|
|
715
|
+
if (command === 'fetch') result = await runFetch(items, opts);
|
|
716
|
+
else { const { items: out, sheet } = await remoteSheet(items, opts); result = { count: out.length, items: out, ...sheet }; }
|
|
717
|
+
}
|
|
718
|
+
const { failed, ...output } = result;
|
|
719
|
+
console.log(JSON.stringify({ ...output, elapsedMs: Date.now() - start }, null, 2));
|
|
720
|
+
if (failed) process.exitCode = 1;
|
|
721
|
+
}
|
|
722
|
+
// Compare real paths: through a symlinked install or /tmp on macOS, the URL and argv differ and the script would exit silently.
|
|
723
|
+
const real = file => { try { return realpathSync(file); } catch { return path.resolve(file); } };
|
|
724
|
+
if (process.argv[1] && real(process.argv[1]) === real(fileURLToPath(import.meta.url))) {
|
|
725
|
+
main().catch(err => { console.error(`[curate-media] ${err.message}`); process.exitCode = 1; });
|
|
726
|
+
}
|