elanous 0.2.9 → 0.2.11
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/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/Y4zKzL62sakG5momz3SoQ/_buildManifest.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1159-0c5ef8bc21cb6afb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1394-556de6d71bbdb0fb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1452.93700483b9d4fc61.js +9 -0
- package/apps/pwa/out/_next/static/chunks/{6062-73e68343a9814f6a.js → 1829-b453a1b22d3999bb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2176-c379e3ebce874151.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2310-7a59f043f6db207e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2426.8c6ee0c668d53def.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2548-6e316138d347cefe.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3170-7ec60a56fa0e4728.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3278-ae6ef084769f6195.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3454-988a53dac60a1763.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3638-aa80b4641ea439db.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3735-fe3402553ac1c9c6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3869-a39d1d9921e10d43.js +1 -0
- package/apps/pwa/out/_next/static/chunks/390.c373e31b3b9d0c41.js +5 -0
- package/apps/pwa/out/_next/static/chunks/3992-2e522a8ac5f2afde.js +25 -0
- package/apps/pwa/out/_next/static/chunks/4215-c9d91dd644ec1719.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4508-5d0161d1e724d735.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4685-c3c1c625b71accd1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4733-623a79345b171e1c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4883.3bd969ea1d3aaaff.js +2 -0
- package/apps/pwa/out/_next/static/chunks/507-5acd98bd6dab2d8a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5445-745eb795c5963d79.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5532.6899a12958a89b72.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5845-e6c003ce8d5fd36a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5978-b27004c677119115.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8046cadf-211ca3c0e00f2b72.js → 5cd7e3a9-404fd6b8d0ecd44c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6048-4c5c33579bc4ef78.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6303-422dbf0f67c72aac.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6751-bb3595948b2e311b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7274-19b7279f18b47ab3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4323.c5dfb6f503304775.js → 7334.a6679d9168f37aa8.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7551-a0e0707b79164ddd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7777-79f845045aa1ed86.js +1 -0
- package/apps/pwa/out/_next/static/chunks/79f8be9e-957d68913b590b08.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8025-cc24ee5d8f2fb6f8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8039-df92e08fede5de8f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8206-c6684dcde12be721.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8421-1c4acfb1395a0edf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8605-ea5bee9dfae512c7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8725.bf46bb1395dd0fa2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8734-252d511cd276c0dc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8834-ca1cb09c1cb1a6bf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2415.ed6428ff334c7ce2.js → 9269.d2a4795bc6812717.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9538-2816b90923606459.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9656-c1540088f5859c80.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9906-482c6619b3fdda20.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-d16993889b125c42.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-1dcdc58fc2de2454.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-6a061bd97f0b9398.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-cb95a7432c9ee744.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-f972fc1c00a62e88.js → page-b12b6d2f8d2d1aea.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-b9efe868bfd36255.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-7c97dcdfe2e37a60.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-8fd36984f549270a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-5721537ca8a3598c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-b654240a572e4c48.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-b9750abc897508c3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-4cd1de371f8ad048.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-d99f89657828421c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-d16993889b125c42.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-50cb2a0eb15e6c83.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-c8b1ccda6ba7ae98.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-d6b1d156fb95e74b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-2a9932ddb62d76bd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-9688f5b4f1ff950f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-6b15dd6456c5df09.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-adfbd2ba9598e59a.js → page-35414e0a007b4082.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-e7a529e0507ac86c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-e001823c35c9dd67.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-371423d1a573d64b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-4b764b125339ac17.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-cdf8e4b01067bcf1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-3a24072b35470157.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-94c82eb0ae5abb32.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-0be8d4b2ced75ba0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-d37cb0d52f9b6bd5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-686da0df9bf23e5c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-4dc614f3ce043e63.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-53f80edc6b8dc07c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-d02c782fc792e2af.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-d16993889b125c42.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-239c17b911b84e72.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-dc6a79fb003f746c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-0fffb84cafdc9da6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-1218753c82d585f6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-4b5935e67531efb5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-3285bc87caf1eaa5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-ad553b0e06505ecd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-fca096e3e860306f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-9ebfa78f0d9885c3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-3dc5f21f7e39622d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-c6da2b468454532d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-93bc70e36f595414.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2ea96958-e8c7e112de50b067.js → f48170cc-d05d28bf87a43bf4.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/framework-172630e6aa5e2d7f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-f0fe2d7477845181.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-fc98ac70b44eccb0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-5bd7f10271f72413.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-97d0d85ea05fef9b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-9e4d02d567b98240.js +1 -0
- package/apps/pwa/out/_next/static/css/543f56bcb1f79fca.css +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +23 -23
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +23 -23
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +23 -23
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +22 -22
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +22 -22
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +22 -22
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +23 -23
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +23 -23
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +23 -23
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +23 -23
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +23 -23
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +23 -23
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +22 -22
- package/apps/pwa/out/inside/index.html +1 -0
- package/apps/pwa/out/inside/index.txt +31 -0
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +23 -23
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +23 -23
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +22 -22
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +22 -22
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +23 -23
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +23 -23
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +22 -22
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +22 -22
- package/apps/pwa/out/ops/seats/index.html +1 -0
- package/apps/pwa/out/ops/seats/index.txt +30 -0
- package/apps/pwa/out/outputs/index.html +1 -0
- package/apps/pwa/out/outputs/index.txt +30 -0
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +23 -23
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +23 -23
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +22 -22
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +23 -23
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +23 -23
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +23 -23
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +23 -23
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +23 -23
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +22 -22
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +23 -23
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +23 -24
- package/apps/pwa/out/today/index.html +1 -0
- package/apps/pwa/out/today/index.txt +33 -0
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +23 -23
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +23 -23
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +23 -23
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +23 -24
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +23 -23
- package/graphs/drafts/draft-cleanup.yaml +19 -0
- package/graphs/drafts/recipes.yaml +23 -0
- package/graphs/field/field-feed.yaml +2 -0
- package/graphs/implement-loop.yaml +1 -1
- package/graphs/intake/intake-daily.yaml +2 -1
- package/graphs/ops/recipes.yaml +3 -0
- package/graphs/ops/secret-exposure.yaml +24 -0
- package/graphs/release/recipes.yaml +3 -0
- package/graphs/release/release-loop.yaml +4 -1
- package/graphs/video/explainer-line.yaml +2 -2
- package/package.json +2 -1
- package/plugins/card-followup/graphs/run-step.ts +48 -13
- package/plugins/doc-draft/graphs/doc-draft.yaml +3 -3
- package/plugins/doc-draft/graphs/run-step.ts +13 -6
- package/plugins/elanous-hwp/README.md +25 -0
- package/plugins/elanous-hwp/cli.ts +22 -0
- package/plugins/elanous-hwp/converter.ts +237 -0
- package/plugins/elanous-hwp/fixtures/fixed-controls.hwpx +0 -0
- package/plugins/elanous-hwp/fixtures/fixed-table-controls.hwpx +0 -0
- package/plugins/elanous-hwp/fixtures/fixed.hwpx +0 -0
- package/plugins/elanous-hwp/plugin.json +16 -0
- package/plugins/elanous-hwp/plugin.ts +40 -0
- package/scripts/ci-public-export-leak-gate.ts +75 -14
- package/scripts/ci-public-leak-gate.ts +44 -9
- package/scripts/ci-pwa-build-gate.ts +160 -0
- package/scripts/context-hooks/claude-stop.sh +4 -0
- package/scripts/context-hooks/emit.ts +24 -0
- package/scripts/context-hooks/post-tool-use.sh +4 -0
- package/scripts/coord-channel-watch.sh +54 -8
- package/scripts/coord-digest.ts +199 -0
- package/scripts/coord-post.sh +60 -3
- package/scripts/coord-requests-bridge.ts +143 -0
- package/scripts/field-feed/lib.ts +4 -0
- package/scripts/fleet/node-setup.sh +20 -3
- package/scripts/homebrew-formula-bump.ts +69 -0
- package/scripts/install.ps1 +68 -9
- package/scripts/install.sh +39 -12
- package/scripts/lib/pwa-scenarios/scenarios.ts +20 -0
- package/scripts/lib/tui-regress-checks.ts +146 -0
- package/scripts/marketing/measure-site-numbers.ts +169 -0
- package/scripts/measure-gate-shards.ts +309 -0
- package/scripts/memory-lifecycle-cycle.ts +2 -2
- package/scripts/mission-request-judge.ts +31 -1
- package/scripts/morning-report.ts +2 -2
- package/scripts/opportunity-scan.ts +1 -1
- package/scripts/ops-now-probe.ts +327 -0
- package/scripts/publish-internal-dist.ts +11 -1
- package/scripts/release-loop/auto-approve-node.ts +21 -4
- package/scripts/release-loop/cut-branch.ts +84 -0
- package/scripts/release-loop/cutoff.ts +19 -8
- package/scripts/release-loop/gate-node.ts +14 -13
- package/scripts/release-loop/mac-smoke-node.ts +229 -0
- package/scripts/release-loop/node-verdict.ts +3 -3
- package/scripts/release-loop/ops-upgrade-node.ts +72 -16
- package/scripts/release-loop/release-readiness.ts +9 -0
- package/scripts/release-loop/tui-node-checks.ts +42 -0
- package/scripts/release-loop/tui-sim-node.ts +32 -8
- package/scripts/release-loop/unattended-release.ts +5 -3
- package/scripts/release-loop/version-node.ts +112 -13
- package/scripts/repo-watch-cycle.ts +1 -1
- package/scripts/retro-cycle.ts +2 -2
- package/scripts/schedule-health-report.ts +1 -1
- package/scripts/signal-digest-cycle.ts +2 -7
- package/scripts/signal-digest-photo.ts +11 -0
- package/scripts/storyboard/storyboard.ts +5 -161
- package/scripts/tech1-public-measure.ts +94 -0
- package/scripts/test-diet/lib.ts +30 -1
- package/scripts/test-diet/node.ts +83 -8
- package/scripts/test-timeout-audit.ts +114 -0
- package/scripts/tree-sync-alert.ts +1 -1
- package/scripts/tui-regress.ts +71 -0
- package/scripts/ux/demo-rehearsal.ts +243 -0
- package/scripts/ux/lib/rehearsal-verdict.ts +73 -0
- package/scripts/waitlist-daily-digest.ts +1 -1
- package/skills/diagram-master/SKILL.md +366 -0
- package/skills/diagram-master/references/engines/excalidraw/color-palette.md +67 -0
- package/skills/diagram-master/references/engines/excalidraw/element-templates.md +182 -0
- package/skills/diagram-master/references/engines/excalidraw/json-schema.md +71 -0
- package/skills/diagram-master/references/engines/excalidraw/render_excalidraw.py +189 -0
- package/skills/diagram-master/references/engines/excalidraw/render_template.html +57 -0
- package/skills/diagram-master/references/engines/matplotlib/matplotlib-patterns.md +235 -0
- package/skills/diagram-master/references/engines/matplotlib/render_matplotlib.py +184 -0
- package/skills/diagram-master/references/engines/mermaid/domain-templates.md +435 -0
- package/skills/diagram-master/references/engines/mermaid/render_mermaid.py +116 -0
- package/skills/diagram-master/references/engines/mermaid/syntax-reference.md +2247 -0
- package/skills/diagram-master/references/engines/nanobannana/render_nanobannana2.py +168 -0
- package/skills/diagram-master/references/engines/svg/color-palette.md +188 -0
- package/skills/diagram-master/references/engines/svg/html-base-template.html +332 -0
- package/skills/diagram-master/references/engines/svg/interactivity-patterns.md +435 -0
- package/skills/diagram-master/references/engines/svg/render_interactive_svg.py +212 -0
- package/skills/diagram-master/references/engines/svg/svg-element-templates.md +349 -0
- package/skills/diagram-master/references/examples/README.md +69 -0
- package/skills/diagram-master/references/examples/ref_grid_architecture.png +0 -0
- package/skills/diagram-master/references/examples/ref_layered_flow.png +0 -0
- package/skills/diagram-master/references/pyproject.toml +16 -0
- package/skills/diagram-master/references/quality-checklist.md +33 -0
- package/skills/diagram-master/references/routing-guide.md +164 -0
- package/skills/diagram-master/references/uv.lock +1356 -0
- package/skills/explainer-video/SKILL.md +4 -0
- package/skills/explainer-video/engine/reel.mjs +18 -13
- package/skills/explainer-video/engine/reel.sh +24 -5
- package/skills/explainer-video/engine/stitch.mjs +145 -0
- package/skills/google-workspace/SKILL.md +33 -0
- package/skills/google-workspace/scripts/gws-safe.sh +177 -0
- package/skills/grill-me/SKILL.md +1 -1
- package/skills/lecture-note-digitizer/SKILL.md +926 -0
- package/skills/lecture-note-digitizer/references/convert_to_pdf.py +30 -0
- package/skills/lecture-note-digitizer/references/extract_slide_diagram.py +73 -0
- package/skills/lecture-note-digitizer/references/html-template.md +167 -0
- package/skills/lecture-note-digitizer/references/katex-style-guide.md +165 -0
- package/skills/omni-crawl/SKILL.md +21 -11
- package/skills/omni-crawl/scripts/main.ts +6 -6
- package/skills/omni-crawl/src/router.ts +3 -3
- package/skills/omni-digest/SKILL.md +17 -9
- package/skills/omni-digest/digest.md +2 -2
- package/skills/omni-digest/src/fetch.ts +30 -3
- package/skills/pitch-deck/SKILL.md +36 -0
- package/skills/pitch-deck/template.html +80 -0
- package/skills/youtube-master/SKILL.md +163 -0
- package/skills/youtube-master/package-lock.json +558 -0
- package/skills/youtube-master/package.json +13 -0
- package/skills/youtube-master/references/study-note-template.md +124 -0
- package/skills/youtube-master/scripts/main.ts +435 -0
- package/skills/youtube-master/scripts/transcribe-local.ts +24 -0
- package/skills/youtube-master/src/audio.ts +63 -0
- package/skills/youtube-master/src/domain-staging.ts +50 -0
- package/skills/youtube-master/src/env.ts +36 -0
- package/skills/youtube-master/src/obsidian.ts +155 -0
- package/skills/youtube-master/src/router.ts +110 -0
- package/skills/youtube-master/src/stt.ts +199 -0
- package/skills/youtube-master/src/study-note.ts +175 -0
- package/skills/youtube-master/src/summarize.ts +358 -0
- package/skills/youtube-master/src/transcript.ts +76 -0
- package/skills/youtube-master/src/types.ts +91 -0
- package/skills/youtube-master/src/util.ts +93 -0
- package/skills/youtube-master/src/youtube.ts +70 -0
- package/skills/youtube-master/tsconfig.json +13 -0
- package/src/acp/codex-approval-adapter.ts +9 -3
- package/src/acp/core-turn-bridge.ts +117 -18
- package/src/acp/fact-question.ts +11 -0
- package/src/acp/server.ts +53 -9
- package/src/acp/tool-approval.ts +126 -0
- package/src/acp/turn-runner.ts +36 -0
- package/src/agent/monad-agent-turn.ts +8 -0
- package/src/agent-mission/driver.ts +80 -10
- package/src/agent-mission/pty-decision.ts +71 -0
- package/src/agent-mission/review-loop.ts +30 -0
- package/src/ask-user-question/pending-questions.ts +25 -10
- package/src/ask-user-question/types.ts +9 -0
- package/src/auth/setup-link-tokens.ts +16 -5
- package/src/autopilot/build/integrity-gate.ts +12 -0
- package/src/autopilot/build/llm-conflict-merge.ts +16 -3
- package/src/autopilot/tool-registry.ts +2 -0
- package/src/boot/daemon-prompt-turn.ts +8 -0
- package/src/boot/daemon-runtime.ts +9 -3
- package/src/boot/daemon-tools/index.ts +8 -0
- package/src/boot/daemon-tools/types.ts +2 -0
- package/src/boot/daemon-tools/web-search.ts +4 -0
- package/src/card-followup/core.ts +109 -0
- package/src/chat/ctrl-c-in-input.ts +14 -0
- package/src/chat/index.ts +53 -0
- package/src/chat/pickers/state.ts +6 -2
- package/src/cli/agent-cli.ts +7 -0
- package/src/cli/decisions-cli.ts +25 -3
- package/src/cli/doctor-bundle-upload.ts +68 -0
- package/src/cli/doctor-cli.ts +38 -4
- package/src/cli/drive-input-jail.ts +101 -0
- package/src/cli/hooks-cli.ts +17 -3
- package/src/cli/make-cli.ts +43 -0
- package/src/cli/ops-cli.ts +20 -0
- package/src/cli/persona-cli.ts +90 -4
- package/src/cli/plugin-cli.ts +31 -22
- package/src/cli/pod-cli.ts +61 -2
- package/src/cli/pr-cli.ts +82 -17
- package/src/cli/pty-drive-cli.ts +41 -7
- package/src/cli/pty-drive-verdict.ts +40 -0
- package/src/cli/pwa-stop.ts +163 -13
- package/src/cli/pwa-test.ts +3 -0
- package/src/cli/release-cli.ts +80 -24
- package/src/cli/self-update.ts +14 -0
- package/src/cli/setup-cli.ts +43 -1
- package/src/cli/skills-cli.ts +17 -1
- package/src/cli/storyboard-cli.ts +37 -0
- package/src/cli/test-state-dir-flag.ts +11 -4
- package/src/connectors/linear.ts +146 -0
- package/src/context-bus/claude-hooks-install.ts +10 -0
- package/src/context-bus/context-now-surfaces.ts +48 -0
- package/src/context-bus/context-now.ts +104 -0
- package/src/context-bus/coord-events.ts +115 -0
- package/src/context-bus/external-events.ts +52 -0
- package/src/context-bus/long-term-memory.ts +108 -0
- package/src/dashboard/dashboard-boot-fixture.ts +23 -0
- package/src/dashboard/index.ts +63 -21
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +14 -1
- package/src/dashboard/slash-runtime/help-from-registry.ts +43 -28
- package/src/decisions/decision-cards.ts +58 -3
- package/src/decisions/decision-ledger.ts +85 -6
- package/src/decisions/decision-linear-projection.ts +157 -0
- package/src/decisions/web-push-decision-cards.ts +50 -0
- package/src/directive/interpretation-card.ts +36 -0
- package/src/discord/sprint21-runtime.ts +5 -1
- package/src/discord-self-message.ts +32 -0
- package/src/discord-slash-wire.ts +5 -1
- package/src/discord.ts +19 -2
- package/src/domains/coo-admin-tool.ts +62 -0
- package/src/domains/core-tools.ts +13 -4
- package/src/domains/elanous-card.ts +29 -0
- package/src/domains/ops-facts-tool.ts +76 -0
- package/src/domains/outbound-alert.ts +46 -13
- package/src/domains/release-tool.ts +168 -0
- package/src/domains/telegram-kind-route.ts +65 -9
- package/src/exec-requests/default-outputs.ts +25 -0
- package/src/exec-requests/planner.ts +106 -28
- package/src/exec-requests/runner.ts +123 -12
- package/src/exec-requests/store.ts +11 -2
- package/src/execution-loop/launch-gate.ts +100 -0
- package/src/field/field-media.ts +10 -7
- package/src/field/field-reel.ts +19 -5
- package/src/field-note/index.ts +201 -0
- package/src/field-note/transcribe.ts +248 -0
- package/src/git-fs/worktree.ts +45 -6
- package/src/graph-kinds/registry.ts +3 -3
- package/src/graph-runner/graph-approval-card.ts +113 -0
- package/src/graph-runner/graph-cli.ts +189 -4
- package/src/graph-runner/graph-grow-llm.ts +37 -0
- package/src/graph-runner/graph-grow.ts +47 -0
- package/src/graph-runner/graph-notify.ts +25 -3
- package/src/graph-runner/runner.ts +453 -35
- package/src/grok/agent-search.ts +8 -2
- package/src/guardian/secret-exposure-scan.ts +101 -0
- package/src/harness/browser-verify.ts +25 -2
- package/src/harness/harness-cli-command.ts +478 -42
- package/src/harness/harness-incidents-writer.fixture.ts +24 -0
- package/src/harness/harness-incidents.ts +129 -0
- package/src/harness/harness-pod-dispatch.ts +43 -23
- package/src/harness/harness-seams.ts +38 -4
- package/src/harness/harness-stop.ts +75 -6
- package/src/harness/launch-registry.ts +91 -0
- package/src/harness/process-garbage.ts +5 -3
- package/src/hitl/clarification-policy.ts +3 -1
- package/src/hooks/dispatch.ts +82 -0
- package/src/hooks/expose.ts +254 -0
- package/src/hooks/github-shadow.ts +108 -0
- package/src/hooks/providers.ts +71 -5
- package/src/hooks/queue.ts +1 -1
- package/src/hooks/receiver.ts +21 -20
- package/src/index.ts +206 -49
- package/src/intake-plane/author-goals.ts +37 -4
- package/src/intake-plane/categorize.ts +18 -46
- package/src/intake-plane/check.ts +3 -0
- package/src/intake-plane/discord-seat-work.ts +57 -2
- package/src/intake-plane/front-route-classifier.ts +33 -39
- package/src/intake-plane/persona-answer.ts +30 -0
- package/src/intake-plane/runtime-callables.ts +8 -1
- package/src/intake-plane/seat-doc-route.ts +170 -6
- package/src/intake-plane/telegram-seat-work.ts +80 -4
- package/src/intake-plane/telegram-work.ts +1 -0
- package/src/intake-plane/wish-folder.ts +67 -0
- package/src/intent-gate/gate.ts +30 -10
- package/src/live/detail-switch.ts +5 -0
- package/src/llm/difficulty.ts +57 -0
- package/src/llm/judge-layer.ts +101 -0
- package/src/llm/mission-router.ts +33 -19
- package/src/loops/contract/examples/cmo-seat.loop.yaml +51 -0
- package/src/loops/contract/examples/orchestrator.loop.yaml +57 -0
- package/src/loops/contract/examples/steward.loop.yaml +50 -0
- package/src/loops/contract/loop-agent.schema.json +138 -0
- package/src/loops/contract/validate.ts +64 -0
- package/src/loops/registry.ts +20 -6
- package/src/market/signed-index.ts +4 -0
- package/src/maturity/bot-command-maturity.ts +45 -0
- package/src/maturity/cli-maturity.ts +34 -0
- package/src/maturity/feature-maturity.ts +120 -1
- package/src/maturity/tui-slash-maturity.ts +31 -0
- package/src/memory.ts +32 -4
- package/src/model-tier/index.ts +2 -2
- package/src/model-tier/tier-resolver.ts +20 -20
- package/src/model-tier/types.ts +6 -6
- package/src/mss/logging/log-store.ts +15 -0
- package/src/nexus/api/config-model-tier.ts +5 -4
- package/src/nexus/api/exec-requests.ts +33 -3
- package/src/nexus/api/fabric-plans.ts +53 -0
- package/src/nexus/api/harness-api.ts +5 -1
- package/src/nexus/api/health.ts +2 -0
- package/src/nexus/api/http-server.ts +89 -1
- package/src/nexus/api/inside-events.ts +226 -0
- package/src/nexus/api/meta-api.ts +32 -8
- package/src/nexus/api/missions.ts +6 -1
- package/src/nexus/api/ops-api.ts +10 -1
- package/src/nexus/api/ops-seats-sources.ts +37 -7
- package/src/nexus/api/ops-seats.ts +49 -6
- package/src/nexus/api/outputs-route.ts +44 -0
- package/src/nexus/api/seat-requests.ts +132 -10
- package/src/nexus/api/sessions-store.ts +42 -0
- package/src/nexus/api/setup-codex-login.ts +160 -0
- package/src/nexus/api/sub-seat-charters.ts +34 -0
- package/src/nexus/api/tabs-mutations.ts +3 -1
- package/src/nexus/api/trace.ts +18 -6
- package/src/nexus/api/usage-api.ts +102 -0
- package/src/nexus/api/workflow-run-event-bridge.ts +1 -0
- package/src/nexus/api/workflows.ts +80 -6
- package/src/nexus/index.ts +47 -4
- package/src/nexus/kinds/webterm.ts +2 -1
- package/src/nexus/webterm/pty.ts +6 -2
- package/src/onboarding/cli-auth-status.ts +80 -0
- package/src/onboarding/first-run.ts +89 -0
- package/src/onboarding/lan-address.ts +45 -0
- package/src/onboarding/voice-ai.ts +3 -3
- package/src/onboarding/web-first.ts +119 -24
- package/src/onboarding.ts +4 -0
- package/src/ops-now/ops-now-refresh.ts +100 -0
- package/src/org/seat-budget.ts +48 -0
- package/src/org/stall-escalation.ts +63 -0
- package/src/outputs/ledger.ts +120 -0
- package/src/persona/persona-todo.ts +79 -0
- package/src/persona/presets.ts +116 -2
- package/src/persona/team-set.ts +155 -0
- package/src/plugins/install/plugin-install.ts +14 -0
- package/src/plugins/maker/market-bundle.ts +109 -0
- package/src/plugins/maker/plugin-maker.ts +254 -11
- package/src/plugins/maker/wizard-generate.ts +103 -0
- package/src/pod-lease/admission.ts +150 -0
- package/src/pod-lease/dependency-state.ts +85 -0
- package/src/pod-lease/host-lease.ts +127 -0
- package/src/project/project-store.ts +115 -0
- package/src/project/project-suggestion.ts +16 -0
- package/src/project/session-context.ts +24 -0
- package/src/release-loop/checklist.ts +79 -5
- package/src/release-loop/feature-store.ts +159 -24
- package/src/release-loop/next-md-merge.ts +58 -0
- package/src/release-loop/release-note.ts +59 -4
- package/src/release-loop/release-schedule.ts +53 -0
- package/src/repl/index.ts +7 -0
- package/src/seat-dispatch/ceo-commands.ts +41 -8
- package/src/seat-dispatch/ceo-intent.ts +38 -0
- package/src/seat-dispatch/seat-ask.ts +130 -0
- package/src/seat-dispatch/seat-questions.ts +100 -0
- package/src/seat-loop/seat-loop.ts +750 -0
- package/src/seat-loop/seat-report.ts +99 -0
- package/src/self-dev/decompose.ts +66 -11
- package/src/self-dev/dev-cli.ts +4 -1
- package/src/self-dev/dev-pipeline.ts +37 -1
- package/src/self-dev/draft-sweep.ts +38 -33
- package/src/self-dev/draft-triage-rules.ts +8 -5
- package/src/self-dev/fabric-plan-core.ts +297 -0
- package/src/self-dev/orchestrate.ts +36 -4
- package/src/self-dev/release-path-guard.ts +62 -0
- package/src/self-dev/run-store.ts +10 -1
- package/src/self-dev/shard-aggregate.ts +230 -0
- package/src/self-dev/shard-placement.ts +60 -0
- package/src/self-dev/shard-plan.ts +48 -0
- package/src/self-dev/shard-pod-run.ts +77 -0
- package/src/self-dev/shard-run.ts +105 -0
- package/src/self-implement/execution-clarification.ts +115 -0
- package/src/self-implement/gate-baseline.ts +1 -1
- package/src/self-implement/gate-cli.ts +46 -4
- package/src/self-implement/gate-scope.ts +37 -7
- package/src/self-implement/graph-variant.ts +171 -0
- package/src/self-implement/harness-policy-gates.ts +1 -1
- package/src/self-implement/headless-elanous-driver.ts +22 -3
- package/src/self-implement/host-regate.ts +22 -1
- package/src/self-implement/importer-test-index.ts +2 -1
- package/src/self-implement/l8-merge-queue.ts +130 -0
- package/src/self-implement/orchestrator.ts +279 -12
- package/src/self-implement/pipeline-shape.ts +1 -1
- package/src/self-implement/route-consumer-test-index.ts +122 -0
- package/src/self-implement/run-status-mapping.ts +5 -3
- package/src/self-implement/seams.ts +72 -11
- package/src/self-implement/test-seams.ts +1 -0
- package/src/self-implement/uncommitted-work.ts +59 -0
- package/src/session/chat.ts +2 -0
- package/src/session/index.ts +45 -13
- package/src/shell-env-bootstrap.ts +17 -0
- package/src/skills/gift-install.ts +215 -0
- package/src/skills/tools/web-search.ts +4 -1
- package/src/steward/directive.ts +51 -2
- package/src/steward/launch.ts +266 -0
- package/src/steward/outcome-notify.ts +115 -0
- package/src/steward/steward-cards.ts +25 -11
- package/src/steward/triage-plan.ts +32 -4
- package/src/steward/triage.ts +129 -13
- package/src/steward/working-backwards.ts +64 -0
- package/src/storyboard/storyboard.ts +141 -0
- package/src/task-cards/card-cli.ts +25 -1
- package/src/task-cards/card-store.ts +2 -2
- package/src/task-orchestrator/mission.ts +5 -0
- package/src/task-orchestrator/store.ts +12 -3
- package/src/task-orchestrator/surfaces/pod-command-job.ts +29 -7
- package/src/task-orchestrator/surfaces/pod-lease.ts +397 -0
- package/src/task-orchestrator/surfaces/pod-pool.ts +127 -26
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +214 -40
- package/src/task-orchestrator/surfaces/self-implement.ts +15 -2
- package/src/task-orchestrator/types.ts +2 -0
- package/src/telegram-card-followup.ts +153 -0
- package/src/telegram-commands.ts +10 -1
- package/src/telegram-project-command.ts +71 -0
- package/src/telegram-report.ts +29 -19
- package/src/telegram-run.ts +7 -0
- package/src/telegram.ts +58 -11
- package/src/tool-runtime/self-cognition-runtimes.ts +13 -3
- package/src/tool-runtime/types.ts +4 -0
- package/src/tui-client/dashboard-session.ts +2 -0
- package/src/user-config.ts +216 -27
- package/src/version/packed-revision.json +1 -1
- package/src/web-push/notify-exec-request.ts +32 -0
- package/src/web-search/grok.ts +7 -1
- package/src/web-search/index.ts +28 -1
- package/src/web-search/provider.ts +4 -0
- package/src/web-search/x-intent.ts +5 -0
- package/src/workflow-runtime/executor.ts +277 -47
- package/src/workflow-runtime/index.ts +1 -0
- package/src/workflow-runtime/nodes/subworkflow.ts +60 -0
- package/src/workflow-runtime/plugin-kind-node.ts +1 -1
- package/src/workflow-runtime/schema.ts +79 -4
- package/src/workflow-runtime/types.ts +35 -4
- package/apps/pwa/out/_next/static/bFI5EEwg7T6aG88ibX4Zq/_buildManifest.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1201-09613bce624467c0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1377-1efeffbbac0963ff.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1470.98a6ab61ccb4131c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2375-cb6debea56cce4fb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2929-617f1e66a55be184.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2977-e38442e86289f1d3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2982-2a2df543be3d2e5a.js +0 -25
- package/apps/pwa/out/_next/static/chunks/3403.4bc97b4066945a94.js +0 -5
- package/apps/pwa/out/_next/static/chunks/3441-a1dd4acfeda14488.js +0 -1
- package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5080-1613752891fcadb4.js +0 -10
- package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5791-bb82743fa6299562.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6090-85a3f05f954e5697.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6284-69db415193d5f8bd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6320-baac91d68843a9a6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6416-505ba707cacafbba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7032-f4ef698518fba61c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7647.2fcd30d894d5ee30.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8067-d976b441a104ab74.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8519-5632377cce994f86.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8619-4365e03a2862830f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8630-cdfc61e82c653ffc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8894-e263b332208e3957.js +0 -1
- package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9454-139040d37c79dbc9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9651-3cd291a6fa1370d2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9740-8f5ff96c522b5f55.js +0 -1
- package/apps/pwa/out/_next/static/chunks/ae787e4c-f2998de7f1ba72a4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-fb7c2d458f32fcad.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-35e2713597dd24d1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-918b656f064577ba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-5f30e00693527ebd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-7fbae173b3c103a7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-3b4edcce364062ea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-c5794f484c251f44.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-1130d8eefcd87e76.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-973996d584f008c1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-016e2bda04eecb1f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-b4d6d9a3b56f2940.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-ec49a466121233ba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-f72db356744ceade.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-a6fd9b50d1b85378.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-8b0402a46a749a3d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-037f83e2a1edc8e2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-46fedff57d4bec67.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-0cf318e9dcee2bbf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-899b327cb8b6845c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-6c1e6ba743a29dfe.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-1a8e4807292f6b17.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-28deba6bcb834916.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-de68e2205794037d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-c71c33a573230672.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-0df30e0c7e50148a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-4fc56790fe0d2682.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-59df2aebb38a7c1e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-4647be5dbcbd0795.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-e025892bda74b743.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-a0955de73b89f078.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-369846d2921af584.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-72cbc1c8cdd0f65d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-77b28bfb0996d5ea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-3220c4934f49c6c2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-90670589b87abea8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-7df9243ed021a4cc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-4498c3853f4f688d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-35bedde6d885f08f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-6870f30ea99f8346.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-e5bea36f183b25e6.js +0 -1
- package/apps/pwa/out/_next/static/css/f92f63a5df0ba229.css +0 -1
- package/src/dashboard/turn-typeahead-echo.ts +0 -23
- /package/apps/pwa/out/_next/static/{bFI5EEwg7T6aG88ibX4Zq → Y4zKzL62sakG5momz3SoQ}/_ssgManifest.js +0 -0
|
@@ -91,8 +91,12 @@ The reference is used only for its structure (header, chapter cards, a diagram t
|
|
|
91
91
|
|
|
92
92
|
```bash
|
|
93
93
|
zsh $SKILL/engine/reel.sh <folder> --title "마케터의 밤" --sub "2026.10.02 · 서울"
|
|
94
|
+
zsh $SKILL/engine/reel.sh --warm-cards --title "마케터의 밤" --sub "2026.10.02 · 서울" # 행사 전 카드 미리 굽기
|
|
95
|
+
zsh $SKILL/engine/reel.sh <folder> --title "마케터의 밤" --sub "2026.10.02 · 서울" --instant # 사진 구간만 렌더
|
|
94
96
|
```
|
|
95
97
|
|
|
98
|
+
즉석판 카드 캐시: `~/.cache/elanous-explainer/cards/<sha256(JSON 배열 [title, sub, 1080, 1920, 카드판번호]) 앞 16자>/{intro,end}.mp4` (`EXPLAINER_CACHE`로 루트 변경 가능).
|
|
99
|
+
|
|
96
100
|
- **Input**: a folder of phone photos (jpg · png · heic) and short clips (mp4 · mov). The default upload folder is `~/.elanous/field/<event>/`.
|
|
97
101
|
- **Order**: by capture time (`mdls` creation date), then by file name.
|
|
98
102
|
- **Captions**: optional `<folder>/captions.txt`, one line per item as `파일명 | 자막`. A photo without a line gets one line from a vision model (`codex exec -i`, all photos in parallel, 30 s cap each); if that fails it shows «현장 N». `FIELD_REEL_VISION=0` turns the vision step off.
|
|
@@ -12,15 +12,18 @@ import { join, dirname, resolve, extname, basename } from "node:path";
|
|
|
12
12
|
import { homedir } from "node:os";
|
|
13
13
|
import { spawn, spawnSync } from "node:child_process";
|
|
14
14
|
import { fileURLToPath } from "node:url";
|
|
15
|
+
import { normalizeCardTitle } from "./stitch.mjs";
|
|
15
16
|
|
|
16
17
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
17
18
|
const argv = process.argv.slice(2);
|
|
18
19
|
const opt = (k, d) => { const i = argv.indexOf(`--${k}`); return i >= 0 ? argv[i + 1] : d; };
|
|
19
20
|
const folder = resolve(argv.find((a, i) => !a.startsWith("--") && !(i > 0 && argv[i - 1].startsWith("--"))) ?? ".");
|
|
20
21
|
const PART = opt("part", "full");
|
|
22
|
+
if (!["full", "body", "intro", "end"].includes(PART)) throw new Error(`unknown reel part: ${PART}`);
|
|
23
|
+
const CARD = PART === "intro" || PART === "end";
|
|
21
24
|
const MAX = Number(opt("max", 60)), MIN = Number(opt("min", 24));
|
|
22
25
|
const W = 1080, H = 1920, PHOTO = 4.2, CLIP_MAX = 5, XF = 0.5;
|
|
23
|
-
const INTRO = PART === "body" ? 0 : 3.0, END = PART === "body" ? 0 : 3.4;
|
|
26
|
+
const INTRO = PART === "body" || PART === "end" ? 0 : 3.0, END = PART === "body" || PART === "intro" ? 0 : 3.4;
|
|
24
27
|
const out = join(folder, "reel"), hf = join(out, "hf"), media = join(hf, "assets", "media");
|
|
25
28
|
mkdirSync(media, { recursive: true }); mkdirSync(join(hf, "assets", "fonts"), { recursive: true });
|
|
26
29
|
const T0 = Date.now(), lap = (s) => console.log(`reel: ${s} · ${((Date.now() - T0) / 1000).toFixed(1)}s`);
|
|
@@ -37,18 +40,19 @@ const created = (p) => {
|
|
|
37
40
|
return Number.isFinite(t) ? t : statSync(p).mtimeMs;
|
|
38
41
|
};
|
|
39
42
|
const probeDur = (p) => Number(run("ffprobe", ["-v", "error", "-show_entries", "format=duration", "-of", "csv=p=0", p]).stdout.trim()) || 0;
|
|
40
|
-
|
|
43
|
+
let TITLE = opt("title", ""), SUB = opt("sub", "");
|
|
44
|
+
const caps = Object.fromEntries((!CARD && existsSync(join(folder, "captions.txt")) ? readFileSync(join(folder, "captions.txt"), "utf8") : "")
|
|
41
45
|
.split("\n").filter((l) => l.includes("|")).map((l) => l.split("|").map((s) => s.trim())));
|
|
42
|
-
let items = readdirSync(folder).filter((f) => [...IMG, ...VID].includes(extname(f).toLowerCase()))
|
|
46
|
+
let items = CARD ? [] : readdirSync(folder).filter((f) => [...IMG, ...VID].includes(extname(f).toLowerCase()))
|
|
43
47
|
.map((f) => ({ f, p: join(folder, f), video: VID.includes(extname(f).toLowerCase()), t: created(join(folder, f)) }))
|
|
44
48
|
.sort((a, b) => a.t - b.t || a.f.localeCompare(b.f));
|
|
45
|
-
if (!items.length) throw new Error(`no photos or clips in ${folder}`);
|
|
49
|
+
if (!CARD && !items.length) throw new Error(`no photos or clips in ${folder}`);
|
|
46
50
|
|
|
47
51
|
// ── durations: fit into [MIN, MAX] ─────────────────────────────────────────────
|
|
48
52
|
for (const it of items) it.dur = it.video ? Math.min(CLIP_MAX, Math.max(1.5, probeDur(it.p))) : PHOTO;
|
|
49
53
|
const body = () => items.reduce((a, it) => a + it.dur, 0);
|
|
50
54
|
while (INTRO + body() + END > MAX && items.length > 1) items.splice(Math.floor(items.length / 2), 1); // drop from the middle, keep first & last
|
|
51
|
-
if (PART
|
|
55
|
+
if (PART === "full" && INTRO + body() + END < MIN) { const photos = items.filter((i) => !i.video); const need = MIN - (INTRO + body() + END); for (const p of photos) p.dur += need / Math.max(1, photos.length); }
|
|
52
56
|
|
|
53
57
|
// ── media prep: HEIC → jpg · big photos down to 2160 · a small copy for the vision model · clips re-encoded short & muted ──
|
|
54
58
|
let t = INTRO;
|
|
@@ -92,18 +96,17 @@ const oneLine = (s, max) => {
|
|
|
92
96
|
const CAP_PROMPT = "행사 현장 사진이다. 세로 영상 자막으로 쓸 한국어 한 줄을 출력하라 — 18자 이내 · 마침표 없이 · 보이는 장면만 · 사람 이름·외모 묘사·추측 금지 · 화면 속 글자(행사명·회사명)는 읽히면 써도 된다. 그 한 줄만.";
|
|
93
97
|
const HEAD_PROMPT = "같은 행사에서 찍은 사진들이다. 영상 첫 화면 제목으로 쓸 한국어 행사 이름 한 줄을 출력하라 — 16자 이내 · 화면 속 글자에서 읽히는 행사·회사 이름을 우선 · 읽히지 않으면 장면을 짧게(예: «상장 기념식 현장») · 추측 금지. 그 한 줄만.";
|
|
94
98
|
const slugTitle = (s) => !s || /^[a-z0-9]+(?:-[a-z0-9]+)*$/i.test(s);
|
|
95
|
-
let TITLE = opt("title", ""), SUB = opt("sub", "");
|
|
96
99
|
const photos = items.filter((it) => !it.video);
|
|
97
100
|
const VT0 = Date.now();
|
|
98
|
-
const jobs = VISION ? [
|
|
101
|
+
const jobs = VISION && !CARD ? [
|
|
99
102
|
...photos.filter((it) => !caps[it.f]).map(async (it) => { it.vcap = oneLine(await ask(CAP_PROMPT, [join(out, `v${items.indexOf(it)}.jpg`)]), 22); }),
|
|
100
103
|
...(PART !== "body" && slugTitle(TITLE) && photos.length ? [(async () => { TITLE = oneLine(await ask(HEAD_PROMPT, photos.slice(0, 3).map((it) => join(out, `v${items.indexOf(it)}.jpg`))), 20) || TITLE; })()] : []),
|
|
101
104
|
] : [];
|
|
102
105
|
await Promise.all(jobs);
|
|
103
106
|
const visionMs = Date.now() - VT0;
|
|
104
107
|
items.forEach((it, i) => { it.cap = caps[it.f] || it.vcap || (it.video ? "" : `현장 ${i + 1}`); });
|
|
105
|
-
|
|
106
|
-
if (!SUB) { const d = new Date(items[0].t + 9 * 3600e3); SUB = `${d.getUTCFullYear()}.${String(d.getUTCMonth() + 1).padStart(2, "0")}.${String(d.getUTCDate()).padStart(2, "0")}`; }
|
|
108
|
+
TITLE = normalizeCardTitle(TITLE);
|
|
109
|
+
if (!CARD && !SUB) { const d = new Date(items[0].t + 9 * 3600e3); SUB = `${d.getUTCFullYear()}.${String(d.getUTCMonth() + 1).padStart(2, "0")}.${String(d.getUTCDate()).padStart(2, "0")}`; }
|
|
107
110
|
lap(`vision ${jobs.length ? `${items.filter((i) => i.vcap).length}/${photos.filter((p) => !caps[p.f]).length} captions` : "off"} · title «${TITLE}»`);
|
|
108
111
|
|
|
109
112
|
// ── assets (same cache as build.mjs) ───────────────────────────────────────────
|
|
@@ -119,7 +122,7 @@ for (const [rel, url] of Object.entries(ASSETS)) {
|
|
|
119
122
|
// ── music: cut to length · fade in/out · level to about -16 LUFS (the whole mix is music) ──
|
|
120
123
|
const BGM = process.env.FIELD_REEL_BGM || join(CACHE, "bgm", "field.wav");
|
|
121
124
|
let music = "";
|
|
122
|
-
if (PART
|
|
125
|
+
if (PART === "full" && existsSync(BGM)) {
|
|
123
126
|
const r = run("ffmpeg", ["-v", "error", "-y", "-i", BGM, "-t", String(TOTAL), "-af", `afade=t=in:d=0.4,afade=t=out:st=${f(TOTAL - 1.6)}:d=1.6,loudnorm=I=-16:TP=-1.5:LRA=9`, "-ar", "48000", "-ac", "2", join(media, "bgm.wav")]);
|
|
124
127
|
if (r.status === 0) music = "bgm.wav"; else console.error(`reel: music skipped — ${r.stderr.slice(0, 160)}`);
|
|
125
128
|
}
|
|
@@ -165,16 +168,18 @@ const clips = items.map((it, i) => {
|
|
|
165
168
|
return vis + cap;
|
|
166
169
|
}).join("\n");
|
|
167
170
|
|
|
168
|
-
const intro = PART === "body" ? "" : `<section class="clip card" data-start="0" data-duration="${INTRO}" data-track-index="6">`
|
|
171
|
+
const intro = PART === "body" || PART === "end" ? "" : `<section class="clip card" data-start="0" data-duration="${INTRO}" data-track-index="6">`
|
|
169
172
|
+ `<div class="kick ik">현장 스케치</div>${mark(200, "im")}<h1>${words(TITLE, "it")}</h1><div class="line il"></div><p class="is">${esc(SUB)}</p></section>`;
|
|
170
|
-
const end = PART === "body" ? "" : `<section class="clip card" data-start="${f(t)}" data-duration="${END}" data-track-index="6">`
|
|
173
|
+
const end = PART === "body" || PART === "intro" ? "" : `<section class="clip card" data-start="${f(t)}" data-duration="${END}" data-track-index="6">`
|
|
171
174
|
+ `${mark(230, "em")}<h1 class="et">Elanous</h1><p class="et">사진을 올리면, 영상이 됩니다</p><p class="et red">elanous.ai</p></section>`;
|
|
172
|
-
if (PART
|
|
175
|
+
if (PART === "full" || PART === "intro") {
|
|
173
176
|
tw.push(`tl.fromTo(".ik", {opacity:0, letterSpacing:"0.6em"}, {opacity:1, letterSpacing:"0.28em", duration:0.8, ease:"power3.out"}, 0.1);`);
|
|
174
177
|
tw.push(`tl.fromTo(".im", {rotation:-160, scale:0.6, opacity:0}, {rotation:0, scale:1, opacity:1, duration:1.0, ease:"power3.out"}, 0.0);`);
|
|
175
178
|
tw.push(`tl.fromTo(".it", {opacity:0, y:50}, {opacity:1, y:0, duration:0.55, stagger:0.1, ease:"power3.out"}, 0.45);`);
|
|
176
179
|
tw.push(`tl.fromTo(".il", {scaleX:0}, {scaleX:1, duration:0.6, ease:"power3.inOut"}, 0.9);`);
|
|
177
180
|
tw.push(`tl.fromTo(".is", {opacity:0, y:16}, {opacity:1, y:0, duration:0.45, ease:"power3.out"}, 1.15);`);
|
|
181
|
+
}
|
|
182
|
+
if (PART === "full" || PART === "end") {
|
|
178
183
|
tw.push(`tl.fromTo(".em", {rotation:-200, scale:0.5}, {rotation:0, scale:1, duration:1.2, ease:"power3.out"}, ${f(t)});`);
|
|
179
184
|
tw.push(`tl.fromTo(".et", {opacity:0, y:20}, {opacity:1, y:0, duration:0.5, stagger:0.14, ease:"power3.out"}, ${f(t + 0.5)});`);
|
|
180
185
|
}
|
|
@@ -1,9 +1,28 @@
|
|
|
1
1
|
#!/bin/zsh
|
|
2
|
-
# Field reel
|
|
2
|
+
# Field reel: standard full composition or instant body with cached cards.
|
|
3
3
|
# The layout/contrast check is advisory: phone photos behind captions often miss 3:1, and a failed check must not stop the reel.
|
|
4
|
-
# Usage: zsh reel.sh <folder> [--title "…"] [--sub "…"] (captions: <folder>/captions.txt — `파일 | 자막`)
|
|
5
|
-
E=${0:A:h}
|
|
4
|
+
# Usage: zsh reel.sh <folder> [--title "…"] [--sub "…"] [--instant] (captions: <folder>/captions.txt — `파일 | 자막`)
|
|
5
|
+
E=${0:A:h}
|
|
6
|
+
if [[ $1 == --warm-cards ]]; then
|
|
7
|
+
shift
|
|
8
|
+
node $E/stitch.mjs --warm-cards "$@"
|
|
9
|
+
exit $?
|
|
10
|
+
fi
|
|
11
|
+
F=${1:A}; shift
|
|
6
12
|
[[ -d $F ]] || { print -u2 "reel: no folder $F"; exit 2 }
|
|
7
13
|
T0=$(date +%s)
|
|
8
|
-
|
|
9
|
-
|
|
14
|
+
instant=0
|
|
15
|
+
args=()
|
|
16
|
+
for arg in "$@"; do
|
|
17
|
+
if [[ $arg == --instant ]]; then instant=1; else args+=("$arg"); fi
|
|
18
|
+
done
|
|
19
|
+
rm -rf $F/reel
|
|
20
|
+
if (( instant )); then
|
|
21
|
+
node $E/reel.mjs $F "${args[@]}" --part body &&
|
|
22
|
+
(cd $F/reel/hf && { npx -y hyperframes@${HYPERFRAMES_VERSION:-0.8.95} check >../render.log 2>&1 || print "reel: check reported issues (continuing — user photos vary; see above)" >>../render.log }; npx -y hyperframes@${HYPERFRAMES_VERSION:-0.8.95} render --fps 30 --workers 4 --output ../body.mp4 >>../render.log 2>&1) &&
|
|
23
|
+
node $E/stitch.mjs --instant $F >>$F/reel/render.log 2>&1 || { print -u2 "reel: FAILED — see $F/reel/render.log"; exit 1 }
|
|
24
|
+
print "reel: $F/reel/reel-9x16.mp4 · $(( $(date +%s) - T0 ))s (instant)"
|
|
25
|
+
else
|
|
26
|
+
node $E/reel.mjs $F "${args[@]}" && (cd $F/reel/hf && { npx -y hyperframes@${HYPERFRAMES_VERSION:-0.8.95} check >../render.log 2>&1 || print "reel: check reported issues (continuing — user photos vary; see above)" >>../render.log }; npx -y hyperframes@${HYPERFRAMES_VERSION:-0.8.95} render --fps 30 --workers 4 --output ../reel-9x16.mp4 >>../render.log 2>&1) || { print -u2 "reel: FAILED — see $F/reel/render.log"; exit 1 }
|
|
27
|
+
print "reel: $F/reel/reel-9x16.mp4 · $(( $(date +%s) - T0 ))s"
|
|
28
|
+
fi
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// Pre-render field cards once; render only the photo composition on subsequent runs.
|
|
2
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
3
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { homedir } from 'node:os';
|
|
5
|
+
import { join, dirname, resolve } from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { spawnSync } from 'node:child_process';
|
|
8
|
+
|
|
9
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
const W = 1080, H = 1920, FPS = 30, CARD_VERSION = 1;
|
|
11
|
+
const cacheRoot = () => process.env.EXPLAINER_CACHE || join(homedir(), '.cache', 'elanous-explainer');
|
|
12
|
+
const valid = (path) => { try { const stat = statSync(path); return stat.isFile() && stat.size > 0; } catch { return false; } };
|
|
13
|
+
function run(bin, args, cwd) {
|
|
14
|
+
const result = spawnSync(bin, args, { cwd, encoding: 'utf8', maxBuffer: 4 * 1024 * 1024 });
|
|
15
|
+
if (result.error || result.status !== 0) throw new Error(`${bin}: ${result.error?.message ?? result.stderr?.slice(-2000) ?? `exit ${result.status}`}`);
|
|
16
|
+
return result.stdout;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async function renderCards({ title, sub, intro, end, cacheDir }) {
|
|
20
|
+
for (const [part, output] of [['intro', intro], ['end', end]]) {
|
|
21
|
+
if (valid(output)) continue;
|
|
22
|
+
const project = join(cacheDir, `project-${part}`);
|
|
23
|
+
mkdirSync(project, { recursive: true });
|
|
24
|
+
run('node', [join(here, 'reel.mjs'), project, '--part', part, '--title', title, '--sub', sub]);
|
|
25
|
+
run('npx', ['-y', `hyperframes@${process.env.HYPERFRAMES_VERSION || '0.8.95'}`, 'render', '--fps', String(FPS), '--workers', '4', '--output', output], join(project, 'reel', 'hf'));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const normalizeCardTitle = (title) => !title || /^[a-z0-9]+(?:-[a-z0-9]+)*$/i.test(title) ? '현장 스케치' : title;
|
|
30
|
+
|
|
31
|
+
function ownerGone(lock) {
|
|
32
|
+
let pid;
|
|
33
|
+
try { pid = JSON.parse(readFileSync(join(lock, 'owner.json'), 'utf8')).pid; }
|
|
34
|
+
catch (error) {
|
|
35
|
+
if (error.code !== 'ENOENT' && !(error instanceof SyntaxError)) throw error;
|
|
36
|
+
// A new owner may still be writing its identity. Never reclaim that window.
|
|
37
|
+
try { return Date.now() - statSync(lock).mtimeMs > 5_000; } catch { return false; }
|
|
38
|
+
}
|
|
39
|
+
if (!Number.isSafeInteger(pid) || pid <= 0) return false;
|
|
40
|
+
try { process.kill(pid, 0); return false; }
|
|
41
|
+
catch (error) { return error.code === 'ESRCH'; }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// A single reaper checks identity again under this claim before moving an orphan.
|
|
45
|
+
function recoverLock(lock) {
|
|
46
|
+
const recovery = `${lock}.recovery`;
|
|
47
|
+
try { mkdirSync(recovery); }
|
|
48
|
+
catch (error) {
|
|
49
|
+
if (error.code !== 'EEXIST') throw error;
|
|
50
|
+
if (ownerGone(recovery)) rmSync(recovery, { recursive: true, force: true });
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
writeFileSync(join(recovery, 'owner.json'), JSON.stringify({ pid: process.pid }));
|
|
55
|
+
if (!ownerGone(lock)) return;
|
|
56
|
+
const abandoned = `${lock}.abandoned-${randomUUID()}`;
|
|
57
|
+
try { renameSync(lock, abandoned); }
|
|
58
|
+
catch (error) { if (error.code !== 'ENOENT') throw error; return; }
|
|
59
|
+
rmSync(abandoned, { recursive: true, force: true });
|
|
60
|
+
} finally { rmSync(recovery, { recursive: true, force: true }); }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export async function ensureCards({ title, sub, cacheDir = join(cacheRoot(), 'cards'), render = renderCards }) {
|
|
64
|
+
title = normalizeCardTitle(title);
|
|
65
|
+
const key = createHash('sha256').update(JSON.stringify([title, sub, W, H, CARD_VERSION])).digest('hex').slice(0, 16);
|
|
66
|
+
const dir = join(cacheDir, key);
|
|
67
|
+
const intro = join(dir, 'intro.mp4'), end = join(dir, 'end.mp4');
|
|
68
|
+
const ready = () => valid(intro) && valid(end);
|
|
69
|
+
if (ready()) return { intro, end, cardsCache: 'hit' };
|
|
70
|
+
mkdirSync(cacheDir, { recursive: true });
|
|
71
|
+
const lock = `${dir}.lock`;
|
|
72
|
+
const deadline = Date.now() + 10 * 60_000;
|
|
73
|
+
for (;;) {
|
|
74
|
+
try {
|
|
75
|
+
mkdirSync(lock);
|
|
76
|
+
try { writeFileSync(join(lock, 'owner.json'), JSON.stringify({ pid: process.pid })); }
|
|
77
|
+
catch (error) { rmSync(lock, { recursive: true, force: true }); throw error; }
|
|
78
|
+
break;
|
|
79
|
+
} catch (error) {
|
|
80
|
+
if (error.code !== 'EEXIST') throw error;
|
|
81
|
+
if (ready()) return { intro, end, cardsCache: 'hit' };
|
|
82
|
+
recoverLock(lock);
|
|
83
|
+
if (Date.now() >= deadline) throw new Error(`timed out waiting for card render: ${lock}`);
|
|
84
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
let staging;
|
|
88
|
+
try {
|
|
89
|
+
if (ready()) return { intro, end, cardsCache: 'hit' };
|
|
90
|
+
staging = mkdtempSync(join(cacheDir, `.${key}-`));
|
|
91
|
+
await render({ title, sub, intro: join(staging, 'intro.mp4'), end: join(staging, 'end.mp4'), cacheDir: staging });
|
|
92
|
+
if (!valid(join(staging, 'intro.mp4')) || !valid(join(staging, 'end.mp4'))) throw new Error(`card renderer did not produce both cards: ${dir}`);
|
|
93
|
+
rmSync(dir, { recursive: true, force: true });
|
|
94
|
+
renameSync(staging, dir);
|
|
95
|
+
return { intro, end, cardsCache: 'miss' };
|
|
96
|
+
} finally {
|
|
97
|
+
if (staging) rmSync(staging, { recursive: true, force: true });
|
|
98
|
+
rmSync(lock, { recursive: true, force: true });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function duration(file) {
|
|
103
|
+
const seconds = Number(run('ffprobe', ['-v', 'error', '-show_entries', 'format=duration', '-of', 'default=noprint_wrappers=1:nokey=1', file]).trim());
|
|
104
|
+
if (!Number.isFinite(seconds) || seconds <= 0) throw new Error(`invalid duration: ${file}`);
|
|
105
|
+
return seconds;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function stitch({ intro, body, end, bgm, out }) {
|
|
109
|
+
const first = duration(intro), middle = duration(body), last = duration(end);
|
|
110
|
+
if (Math.min(first, middle, last) <= 0.3) throw new Error('xfade requires clips longer than 0.3s');
|
|
111
|
+
const total = first + middle + last - 0.6;
|
|
112
|
+
const args = ['-v', 'error', '-y', '-i', intro, '-i', body, '-i', end];
|
|
113
|
+
const music = bgm && existsSync(bgm);
|
|
114
|
+
if (music) args.push('-stream_loop', '-1', '-i', bgm);
|
|
115
|
+
const filters = [
|
|
116
|
+
...[0, 1, 2].map((i) => `[${i}:v]fps=${FPS},scale=${W}:${H},setsar=1,format=yuv420p,settb=AVTB,setpts=PTS-STARTPTS[v${i}]`),
|
|
117
|
+
`[v0][v1]xfade=transition=fade:duration=0.3:offset=${(first - 0.3).toFixed(3)}[v01]`,
|
|
118
|
+
`[v01][v2]xfade=transition=fade:duration=0.3:offset=${(first + middle - 0.6).toFixed(3)}[v]`,
|
|
119
|
+
];
|
|
120
|
+
if (music) filters.push(`[3:a]atrim=duration=${total.toFixed(3)},asetpts=PTS-STARTPTS,afade=t=in:d=0.4,afade=t=out:st=${Math.max(0, total - 1.6).toFixed(3)}:d=1.6,loudnorm=I=-16:TP=-1.5:LRA=9[a]`);
|
|
121
|
+
else filters.push(`anullsrc=r=48000:cl=stereo,atrim=duration=${total.toFixed(3)}[a]`);
|
|
122
|
+
mkdirSync(dirname(out), { recursive: true });
|
|
123
|
+
run('ffmpeg', [...args, '-filter_complex', filters.join(';'), '-map', '[v]', '-map', '[a]', '-t', total.toFixed(3), '-r', String(FPS), '-c:v', 'libx264', '-preset', 'veryfast', '-pix_fmt', 'yuv420p', '-c:a', 'aac', '-ar', '48000', '-ac', '2', '-movflags', '+faststart', out]);
|
|
124
|
+
return out;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
128
|
+
const args = process.argv.slice(2);
|
|
129
|
+
const value = (name) => { const i = args.indexOf(name); return i < 0 ? undefined : args[i + 1]; };
|
|
130
|
+
const folder = args[1] && resolve(args[1]);
|
|
131
|
+
if (args[0] === '--warm-cards') {
|
|
132
|
+
const title = value('--title'), sub = value('--sub');
|
|
133
|
+
if (title === undefined || sub === undefined) throw new Error('--warm-cards requires --title and --sub');
|
|
134
|
+
const cards = await ensureCards({ title, sub });
|
|
135
|
+
console.log(`reel: cards ${cards.cardsCache} · ${dirname(cards.intro)}`);
|
|
136
|
+
} else if (args[0] === '--instant' && folder) {
|
|
137
|
+
const timelineFile = join(folder, 'reel', 'timeline.json');
|
|
138
|
+
const timeline = JSON.parse(readFileSync(timelineFile, 'utf8'));
|
|
139
|
+
const cards = await ensureCards({ title: timeline.title, sub: timeline.sub });
|
|
140
|
+
const bgm = process.env.FIELD_REEL_BGM || join(cacheRoot(), 'bgm', 'field.wav');
|
|
141
|
+
stitch({ intro: cards.intro, body: join(folder, 'reel', 'body.mp4'), end: cards.end, bgm, out: join(folder, 'reel', 'reel-9x16.mp4') });
|
|
142
|
+
writeFileSync(timelineFile, JSON.stringify({ ...timeline, cardsCache: cards.cardsCache, music: existsSync(bgm) }, null, 2));
|
|
143
|
+
console.log(`reel: cards ${cards.cardsCache}`);
|
|
144
|
+
} else throw new Error('usage: stitch.mjs --warm-cards --title … --sub … | --instant <folder>');
|
|
145
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: google-workspace
|
|
3
|
+
description: Use your own Google account to find, summarize and draft Gmail messages; read or create Calendar events; read Drive and Sheets.
|
|
4
|
+
requires: []
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Google Workspace
|
|
8
|
+
|
|
9
|
+
## Set up
|
|
10
|
+
|
|
11
|
+
Install the `gws` CLI and run `gws auth login` with **your own Google account**. Grant the scopes required for the requested operation in your account. Do not request or expose credentials in chat.
|
|
12
|
+
|
|
13
|
+
Run commands from this skill directory so the bundled wrapper resolves by relative path:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
bash scripts/gws-safe.sh gmail users messages list --params '{"userId":"me","q":"is:unread","maxResults":10}'
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Use `scripts/gws-safe.sh` for API calls, not `gws` directly. The wrapper removes proxy variables, strips the keyring banner before JSON output, and checks the API method against `gws schema` before running it. A read with an unknown method is blocked too; do not bypass the check. `gws schema <service.resource.method>` can be used to inspect the method locally.
|
|
20
|
+
|
|
21
|
+
## Supported work
|
|
22
|
+
|
|
23
|
+
- Gmail: find messages, read and summarize their contents, compose a draft in the reply, or save an approved draft in Gmail. Saving a draft is a write and requires the write gate below.
|
|
24
|
+
- Calendar: look up events and create events after confirming their details with the user.
|
|
25
|
+
- Drive and Sheets: read files and spreadsheet data. Do not edit them without an explicit request.
|
|
26
|
+
|
|
27
|
+
For example, read Calendar events with `bash scripts/gws-safe.sh calendar events list --params '{"calendarId":"primary"}'`. Check the relevant API schema and required parameters before issuing other calls. Treat API errors as errors, not as empty results.
|
|
28
|
+
|
|
29
|
+
## Write gate and user confirmation
|
|
30
|
+
|
|
31
|
+
Before sending email, deleting anything, or performing another irreversible action, ask the user for explicit confirmation of the **specific action and its targets**. Creating Calendar events and saving Gmail drafts also writes to the account: confirm the details before doing either. The wrapper rejects POST, PUT, PATCH, DELETE and unknown methods with exit code 4 by default; it is not permission to silently enable writes.
|
|
32
|
+
|
|
33
|
+
For each write, first inspect `gws schema <service.resource.method>` and prepare the exact wrapper command, including recipient or calendar, content, and parameters. Show those details to the user and wait for their explicit approval of **that command**. Only after that approval, run the same command yourself from this skill directory with the one-command prefix `ELANOUS_GWS_ALLOW_WRITE=1 bash scripts/gws-safe.sh ...`. Check the JSON response for errors and report the result; do not reuse approval for another command, silently change its arguments, leave the variable enabled for later calls, or bypass the wrapper. If the user does not approve, do not execute the write.
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# 🔐 **`gws` 를 «안전하게» 부르는 한 자리** — ⛔ 이 셸의 함정 둘을 여기서 «한 번만» 막는다.
|
|
3
|
+
#
|
|
4
|
+
# bash scripts/google/gws-safe.sh gmail users messages list --params '{"userId":"me"}'
|
|
5
|
+
#
|
|
6
|
+
# ⛔⭐ **함정 ①: 프록시** — 이 셸엔 HTTPS_PROXY 가 걸려 있고 `gws` 가 그것을 통과 못 한다
|
|
7
|
+
# (문면: "tunnel error: unsuccessful"). ⇒ 프록시를 «빼고» 부른다.
|
|
8
|
+
# 🚨 이 한 줄이 없으면 범위가 멀쩡해도 ***401 로 「인증 실패」처럼 보인다*** — 2026-08-26 에 그렇게 오진할 뻔했다.
|
|
9
|
+
#
|
|
10
|
+
# ⛔⭐ **함정 ②: 머리말** — `gws` 는 "Using keyring backend …" 를 stdout 에 «섞는다».
|
|
11
|
+
# 그대로 파서에 주면 JSON 파싱이 죽고, ***그 죽음이 「API 실패」처럼 보인다***(32차에 실제로 그랬다).
|
|
12
|
+
# ⇒ 첫 `{` 또는 `[` «부터» 잘라 낸다. `grep -v` 로 한 줄만 거르는 것으로는 부족했다.
|
|
13
|
+
#
|
|
14
|
+
# 📌 **왜 이 파일이 «따로» 있나**: 이 지식이 `scripts/botlab/assistant-google.sh` «안»에만 있었다.
|
|
15
|
+
# Google 을 쓰는 두 번째 소비자(스킬)를 만들면서 ***복제하지 않으려고*** 뽑았다.
|
|
16
|
+
# ⛔ 새 소비자는 이 파일을 «부른다» — gws 를 직접 부르지 마라.
|
|
17
|
+
set -uo pipefail
|
|
18
|
+
|
|
19
|
+
if [ "$#" -eq 0 ]; then
|
|
20
|
+
echo "⛔ 인자가 «없다» — gws 에 넘길 명령을 그대로 준다." >&2
|
|
21
|
+
echo " 예: bash scripts/google/gws-safe.sh gmail users messages list --params '{\"userId\":\"me\"}'" >&2
|
|
22
|
+
exit 2
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
# ⛔⭐⭐ **크론의 `PATH` 에는 `gws` 도 `node` 도 «없다»** — 이 저장소가 «여섯 번째» 밟은 자리다.
|
|
26
|
+
# 🚨 2026-08-29 실측: 아침 07:30 회차가 `gws 를 못 찾았다` ×3 으로 죽었다. 손으로 돌리면 «된다».
|
|
27
|
+
# ⛔⛔ 그리고 ***절대 경로만으로는 부족하다*** — `gws` 의 shebang 이 `#!/usr/bin/env node` 라
|
|
28
|
+
# node 가 PATH 에 없으면 ***rc=127***이다(매뉴얼의 ④ 「있는데도 안 된다」 층).
|
|
29
|
+
# 📏 반증: `env -i PATH=/usr/bin:/bin <절대경로>/gws --help` ⇒ rc=127 ·
|
|
30
|
+
# 그 디렉터리를 PATH 에 넣으면 ⇒ rc=0.
|
|
31
|
+
# ⇒ 그래서 ⓐ 실물로 «찾고» ⓑ ***찾은 것의 디렉터리를 PATH 에 넣는다***(node 가 그 옆에 산다).
|
|
32
|
+
resolve_gws_bin() {
|
|
33
|
+
local candidate
|
|
34
|
+
# ⛔⭐ **명시값은 «그것만» 쓴다** — 「이것을 써라」이지 「여기부터 찾아라」가 아니다.
|
|
35
|
+
# 🚨 첫 판은 명시값이 없을 때 자동 탐색으로 «흘러내려» 갔고, 그 바람에
|
|
36
|
+
# ***「gws 가 없다」는 상황을 시험이 만들 수 없었다***(시험이 즉시 잡았다).
|
|
37
|
+
if [ -n "${ELANOUS_GWS_BIN:-}" ]; then
|
|
38
|
+
if [ -x "$ELANOUS_GWS_BIN" ]; then printf '%s\n' "$ELANOUS_GWS_BIN"; return 0; fi
|
|
39
|
+
if command -v "$ELANOUS_GWS_BIN" >/dev/null 2>&1; then command -v "$ELANOUS_GWS_BIN"; return 0; fi
|
|
40
|
+
return 0 # ⛔ 명시했는데 없으면 «없는 것»이다 — 다른 데서 찾지 않는다
|
|
41
|
+
fi
|
|
42
|
+
if command -v gws >/dev/null 2>&1; then command -v gws; return 0; fi
|
|
43
|
+
# ⛔ nvm 은 «버전 디렉터리»가 바뀐다 — 경로를 박지 않고 훑는다(사전순 마지막 = 대개 최신).
|
|
44
|
+
for candidate in "$HOME"/.nvm/versions/node/*/bin/gws /opt/homebrew/bin/gws /usr/local/bin/gws; do
|
|
45
|
+
[ -x "$candidate" ] && printf '%s\n' "$candidate"
|
|
46
|
+
done | tail -1
|
|
47
|
+
}
|
|
48
|
+
GWS_BIN="$(resolve_gws_bin)"
|
|
49
|
+
if [ -z "$GWS_BIN" ]; then
|
|
50
|
+
echo "⛔ gws 를 «못 찾았다»(${ELANOUS_GWS_BIN:-gws}) — 「Google 이 안 된다」가 아니라 ***이 기계에 그 CLI 가 없다***." >&2
|
|
51
|
+
echo " 본 곳: \$ELANOUS_GWS_BIN · PATH · ~/.nvm/versions/node/*/bin · /opt/homebrew/bin · /usr/local/bin" >&2
|
|
52
|
+
exit 3
|
|
53
|
+
fi
|
|
54
|
+
# ⛔ 「찾았다」로 끝내지 않는다 — 그 옆의 node 를 쓸 수 있게 «뒤»에 붙인다.
|
|
55
|
+
# ⚠️ «뒤»인 이유: 부르는 쪽이 준 PATH(시험의 가짜들)를 밀지 않기 위해서다.
|
|
56
|
+
GWS_DIR="$(cd "$(dirname "$GWS_BIN")" 2>/dev/null && pwd)" || GWS_DIR=""
|
|
57
|
+
[ -n "$GWS_DIR" ] && export PATH="$PATH:$GWS_DIR"
|
|
58
|
+
|
|
59
|
+
# ⛔⭐⭐⭐ **열쇠고리(Keychain)를 «쓰는» 순간 크론이 죽는다 — 그래서 저장소를 «파일»로 못 박는다.**
|
|
60
|
+
# 🚨 2026-08-29(36차) 실측 · ***크론 회차의 전체 문면이 스스로 답을 말했다***:
|
|
61
|
+
# 401 Authentication failed: Failed to get token:
|
|
62
|
+
# Error while setting token in cache: OS keyring failed:
|
|
63
|
+
# Platform secure storage failure: ***User interaction is not allowed.***
|
|
64
|
+
# ⇒ Set GOOGLE_WORKSPACE_CLI_KEYRING_BACKEND=file to use file storage.
|
|
65
|
+
# ⇒ 🔑 인증은 «됐고», 갱신한 토큰을 ***Keychain 에 «쓰다가»*** 죽는다. 크론은 비대화형이라
|
|
66
|
+
# 그 쓰기에 필요한 사용자 확인을 못 받는다. ⛔ 「로그인이 안 됐다」가 아니다.
|
|
67
|
+
#
|
|
68
|
+
# ⛔⭐⭐ **그리고 이 축은 `env` 로 «재현되지 않는다»** — 36차가 실제로 속았다:
|
|
69
|
+
# `env -i PATH=/usr/bin:/bin …` 로 돌리면 ***성공한다***(같은 시각 크론은 실패).
|
|
70
|
+
# ***보안 세션은 환경변수를 따라오지 않기 때문***이다. ⇒ 「크론과 같은 PATH 로 돌려 봤다」는
|
|
71
|
+
# 크론 판정이 «아니다». 판정은 ***진짜 크론 회차***로만 한다(RFC §27c ④ 가 옳았다 · 두 번).
|
|
72
|
+
#
|
|
73
|
+
# ⚖️ **보안 결정**: 파일 저장소는 열쇠를 Keychain 대신 «파일»에 둔다. 이 기계는 이미 같은 계급의
|
|
74
|
+
# 결정을 한 번 했다(`~/.config/gws/credentials.json` 평문 · 대표 승인 2026-08-29). 그 디렉터리는
|
|
75
|
+
# `drwx------`(0700)이다. ⛔ 명시값이 있으면 «그것을» 존중한다 — 여기서 덮어쓰지 않는다.
|
|
76
|
+
export GOOGLE_WORKSPACE_CLI_KEYRING_BACKEND="${GOOGLE_WORKSPACE_CLI_KEYRING_BACKEND:-file}"
|
|
77
|
+
|
|
78
|
+
# ⛔ 그래도 열쇠고리 오류가 나면 «이름을 대고» 말한다 — 조용히 401 로 흘리지 않는다.
|
|
79
|
+
diagnose_keyring_401() {
|
|
80
|
+
{
|
|
81
|
+
echo "⛔⭐ 이 401 은 «로그인 실패»가 아니다 — 토큰을 ***저장소에 쓰다가*** 죽었다."
|
|
82
|
+
echo " 지금 백엔드: GOOGLE_WORKSPACE_CLI_KEYRING_BACKEND=${GOOGLE_WORKSPACE_CLI_KEYRING_BACKEND:-(비어 있다)}"
|
|
83
|
+
echo " ⇒ 이 값이 file 이 «아니면» 누가 밖에서 덮어쓴 것이다(이 파일은 file 을 기본으로 준다)."
|
|
84
|
+
echo " ⇒ file 인데도 났다면 ~/.config/gws 의 쓰기 권한을 봐라(0700 이어야 한다)."
|
|
85
|
+
echo " ⛔ 「사람이 다시 로그인하면 된다」로 읽지 마라 — 그 로그인은 이미 돼 있다."
|
|
86
|
+
} >&2
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
# ⛔⭐⭐ **403 을 「범위 부족」으로만 읽지 않는다 — «왜» 그 자격인지가 다른 층에 있다.**
|
|
90
|
+
# 🚨 2026-08-29 실측 · 외부 문서로 확인(deepwiki googleworkspace/cli 2.2-authentication):
|
|
91
|
+
# gws 의 자격 해결 순서는 ***TOKEN/CREDENTIALS_FILE → credentials.enc → credentials.json → ADC*** 이고,
|
|
92
|
+
# 토큰 캐시는 AES-256-GCM 이며 ***그 열쇠가 OS keyring(macOS Keychain)에 산다***.
|
|
93
|
+
# ⇒ 그래서 Keychain 을 «못 읽는» 세션(크론 · 에이전트 하위 프로세스)에서는
|
|
94
|
+
# ***조용히 ADC 로 폴백***하고, ADC 는 gcloud 공용 클라이언트라 Gmail 같은 민감 범위가
|
|
95
|
+
# «이미 차단»돼 있어 ***403 insufficientPermissions*** 가 난다.
|
|
96
|
+
# 📏 이 기계에서 잰 것: `gws auth status` ⇒ `storage=none` · `keyring_backend=keyring` ·
|
|
97
|
+
# `token_cache_exists=true` ⊕ `security find-generic-password -s gws-cli -w` 가 ***프롬프트에 걸려 rc=124***.
|
|
98
|
+
# ⛔ 그러니 「사람이 로그인만 하면 된다」로 안내하면 «틀린 길»로 보낸다 — 그 로그인은 이미 돼 있었다.
|
|
99
|
+
diagnose_403() {
|
|
100
|
+
local storage
|
|
101
|
+
storage="$("$GWS_BIN" auth status 2>/dev/null \
|
|
102
|
+
| sed -n 's/.*"storage" *: *"\([^"]*\)".*/\1/p' | head -1)"
|
|
103
|
+
[ "$storage" = "none" ] || return 0
|
|
104
|
+
{
|
|
105
|
+
echo "⛔⭐ 그런데 이 403 은 «범위만»의 문제가 아닐 수 있다 — 자격 저장소를 «못 읽고» 있다:"
|
|
106
|
+
echo " gws auth status ⇒ storage=none (keyring 을 못 읽었다) ⇒ ***ADC 로 폴백***한 것이다."
|
|
107
|
+
echo " ADC 는 gcloud 공용 클라이언트라 Gmail 등 민감 범위가 «이미 차단»돼 있다."
|
|
108
|
+
echo " ⇒ 사람이 «자기 터미널»에서 한 번(크론·에이전트도 읽을 수 있게 «파일»로 내린다):"
|
|
109
|
+
echo " gws auth export --unmasked > ~/.config/gws/credentials.json && chmod 600 ~/.config/gws/credentials.json"
|
|
110
|
+
echo " ⛔ 그 파일은 «평문 자격»이다 — 두는 것 자체가 보안 결정이다(0600 필수)."
|
|
111
|
+
} >&2
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
# ── 🚧 쓰기 관문 ────────────────────────────────────────────────────────────
|
|
115
|
+
# ⛔⭐⭐ ***되돌릴 수 없는 것을 «규율»로만 막지 않는다.***
|
|
116
|
+
# `google-workspace` 스킬의 SKILL.md 가 「명시 없으면 초안까지」라고 «부탁»하지만,
|
|
117
|
+
# 그것은 프롬프트다 — ***관문이 아니다***. 메일 전송·일정 삭제는 되돌릴 길이 «없다».
|
|
118
|
+
# 📌 브라우저 축이 같은 자리에서 같은 답을 냈다(`decideReversibility` · `actionHosts`).
|
|
119
|
+
#
|
|
120
|
+
# ⭐ **이름을 짐작하지 않는다** — Google 이 «스스로» 말한다:
|
|
121
|
+
# gws schema <service.resource.method> ⇒ "httpMethod": "GET" | "POST" | "DELETE" | …
|
|
122
|
+
# 📏 실측 2026-08-28: 이 조회는 ***지역*** 이다(죽은 프록시로도 답한다) · 165~181ms.
|
|
123
|
+
#
|
|
124
|
+
# ⛔ 모르면 «막는다» — 「모른다」를 「괜찮다」로 읽지 않는다(브라우저 축의 ClickKind 와 같은 규율).
|
|
125
|
+
gws_write_gate() {
|
|
126
|
+
# gws <service> <resource> [sub-resource] <method> [flags] — 첫 «플래그» 앞까지가 위치 인자다.
|
|
127
|
+
local -a pos=()
|
|
128
|
+
local a
|
|
129
|
+
for a in "$@"; do
|
|
130
|
+
case "$a" in -*) break ;; *) pos+=("$a") ;; esac
|
|
131
|
+
done
|
|
132
|
+
# `schema` 자체는 조회다 — 그리고 위치 인자가 모자라면 gws 가 스스로 거절한다.
|
|
133
|
+
if [ "${#pos[@]}" -lt 2 ] || [ "${pos[0]}" = "schema" ] || [ "${pos[0]}" = "auth" ]; then return 0; fi
|
|
134
|
+
|
|
135
|
+
local path
|
|
136
|
+
path=$(IFS=.; echo "${pos[*]}")
|
|
137
|
+
local http
|
|
138
|
+
http=$(env -u HTTP_PROXY -u HTTPS_PROXY -u http_proxy -u https_proxy -u ALL_PROXY -u all_proxy \
|
|
139
|
+
"$GWS_BIN" schema "$path" 2>/dev/null \
|
|
140
|
+
| sed -n '/^[[{]/,$p' \
|
|
141
|
+
| python3 -c 'import sys,json
|
|
142
|
+
try: print((json.load(sys.stdin) or {}).get("httpMethod") or "")
|
|
143
|
+
except Exception: print("")' 2>/dev/null)
|
|
144
|
+
|
|
145
|
+
case "$http" in
|
|
146
|
+
GET|HEAD) return 0 ;;
|
|
147
|
+
"")
|
|
148
|
+
echo "⛔ 이 호출이 «읽기인지 쓰기인지» 못 알아냈다(schema $path 가 httpMethod 를 안 냈다)." >&2
|
|
149
|
+
echo " ⇒ 「모른다」를 「괜찮다」로 읽지 않는다. 읽기라면 경로를 확인하고, 쓰기라면 아래를 보라." >&2
|
|
150
|
+
;;
|
|
151
|
+
*)
|
|
152
|
+
echo "⛔ 이것은 «쓰기»다($http $path) — ***되돌릴 길이 없다***." >&2
|
|
153
|
+
;;
|
|
154
|
+
esac
|
|
155
|
+
echo " 사람이 그 «한 번»을 명시로 열어야 한다: ELANOUS_GWS_ALLOW_WRITE=1 <같은 명령>" >&2
|
|
156
|
+
return 1
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if [ "${ELANOUS_GWS_ALLOW_WRITE:-}" != "1" ]; then
|
|
160
|
+
gws_write_gate "$@" || exit 4
|
|
161
|
+
fi
|
|
162
|
+
|
|
163
|
+
# ⛔ 산출을 «한 번» 담는다 — 403 이면 그 «이유»를 같이 내야 하기 때문이다(diagnose_403).
|
|
164
|
+
# ⚠️ 담지 않으면 파이프 뒤에서 그 판정을 할 수 없다. gws 응답은 작아 이 비용이 싸다.
|
|
165
|
+
GWS_RAW="$(env -u HTTP_PROXY -u HTTPS_PROXY -u http_proxy -u https_proxy -u ALL_PROXY -u all_proxy \
|
|
166
|
+
"$GWS_BIN" "$@" 2>&1)"
|
|
167
|
+
GWS_RC=$?
|
|
168
|
+
printf '%s\n' "$GWS_RAW" | sed -n '/^[[{]/,$p'
|
|
169
|
+
|
|
170
|
+
# ⛔⭐ 403 을 «그냥 흘려보내지 않는다» — 자격 저장소를 못 읽어 ADC 로 폴백한 것일 수 있다.
|
|
171
|
+
# 📌 그 갈림이 없으면 「사람이 로그인만 하면 된다」는 «틀린 길»로 안내하게 된다.
|
|
172
|
+
case "$GWS_RAW" in *'"code": 403'*|*'"code":403'*) diagnose_403 ;; esac
|
|
173
|
+
|
|
174
|
+
# ⛔⭐ 401 + 열쇠고리 문면이면 «다른 병»이다 — 403(범위·ADC 폴백)과 갈라서 말한다.
|
|
175
|
+
case "$GWS_RAW" in *'OS keyring failed'*|*'secure storage failure'*) diagnose_keyring_401 ;; esac
|
|
176
|
+
|
|
177
|
+
exit "$GWS_RC"
|
package/skills/grill-me/SKILL.md
CHANGED
|
@@ -128,7 +128,7 @@ requires: []
|
|
|
128
128
|
| 🧑🤝🧑 **팀 짜기** | 일이 여럿으로 갈릴 때 | ⭐ 목표를 심은 뒤 *"이 일을 할 팀을 짜 보라"* — 인터뷰의 «응용»이다 |
|
|
129
129
|
|
|
130
130
|
⛔ **골 문면으로 갈 때는 파서가 무는 문면을 «그때» 확인한다** —
|
|
131
|
-
|
|
131
|
+
골 저작 방법 매뉴얼 §1a. ⛔ 여기 옮겨 적지 않는다(늙는다).
|
|
132
132
|
|
|
133
133
|
---
|
|
134
134
|
|