jfl 0.0.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 +21 -0
- package/README.md +313 -0
- package/clawdbot-skill/README.md +328 -0
- package/clawdbot-skill/SKILL.md +362 -0
- package/clawdbot-skill/index.ts +486 -0
- package/clawdbot-skill/package.json +28 -0
- package/clawdbot-skill/skill.json +28 -0
- package/dist/commands/agents.d.ts +5 -0
- package/dist/commands/agents.d.ts.map +1 -0
- package/dist/commands/agents.js +399 -0
- package/dist/commands/agents.js.map +1 -0
- package/dist/commands/context-hub.d.ts +12 -0
- package/dist/commands/context-hub.d.ts.map +1 -0
- package/dist/commands/context-hub.js +642 -0
- package/dist/commands/context-hub.js.map +1 -0
- package/dist/commands/deploy.d.ts +5 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +370 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/feedback.d.ts +2 -0
- package/dist/commands/feedback.d.ts.map +1 -0
- package/dist/commands/feedback.js +178 -0
- package/dist/commands/feedback.js.map +1 -0
- package/dist/commands/hud.d.ts +4 -0
- package/dist/commands/hud.d.ts.map +1 -0
- package/dist/commands/hud.js +262 -0
- package/dist/commands/hud.js.map +1 -0
- package/dist/commands/init.d.ts +4 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +553 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/login.d.ts +23 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +818 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/ralph.d.ts +9 -0
- package/dist/commands/ralph.d.ts.map +1 -0
- package/dist/commands/ralph.js +67 -0
- package/dist/commands/ralph.js.map +1 -0
- package/dist/commands/repair.d.ts +7 -0
- package/dist/commands/repair.d.ts.map +1 -0
- package/dist/commands/repair.js +283 -0
- package/dist/commands/repair.js.map +1 -0
- package/dist/commands/session-mgmt.d.ts +33 -0
- package/dist/commands/session-mgmt.d.ts.map +1 -0
- package/dist/commands/session-mgmt.js +404 -0
- package/dist/commands/session-mgmt.js.map +1 -0
- package/dist/commands/session.d.ts +2 -0
- package/dist/commands/session.d.ts.map +1 -0
- package/dist/commands/session.js +639 -0
- package/dist/commands/session.js.map +1 -0
- package/dist/commands/skills.d.ts +31 -0
- package/dist/commands/skills.d.ts.map +1 -0
- package/dist/commands/skills.js +314 -0
- package/dist/commands/skills.js.map +1 -0
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +127 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/synopsis.d.ts +10 -0
- package/dist/commands/synopsis.d.ts.map +1 -0
- package/dist/commands/synopsis.js +277 -0
- package/dist/commands/synopsis.js.map +1 -0
- package/dist/commands/update.d.ts +10 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +165 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/voice.d.ts +410 -0
- package/dist/commands/voice.d.ts.map +1 -0
- package/dist/commands/voice.js +4763 -0
- package/dist/commands/voice.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +512 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/context-hub-mcp.d.ts +11 -0
- package/dist/mcp/context-hub-mcp.d.ts.map +1 -0
- package/dist/mcp/context-hub-mcp.js +548 -0
- package/dist/mcp/context-hub-mcp.js.map +1 -0
- package/dist/telegram/voice.d.ts +146 -0
- package/dist/telegram/voice.d.ts.map +1 -0
- package/dist/telegram/voice.js +351 -0
- package/dist/telegram/voice.js.map +1 -0
- package/dist/types/skills.d.ts +44 -0
- package/dist/types/skills.d.ts.map +1 -0
- package/dist/types/skills.js +5 -0
- package/dist/types/skills.js.map +1 -0
- package/dist/ui/banner.d.ts +18 -0
- package/dist/ui/banner.d.ts.map +1 -0
- package/dist/ui/banner.js +323 -0
- package/dist/ui/banner.js.map +1 -0
- package/dist/ui/index.d.ts +8 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +8 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/prompts.d.ts +52 -0
- package/dist/ui/prompts.d.ts.map +1 -0
- package/dist/ui/prompts.js +72 -0
- package/dist/ui/prompts.js.map +1 -0
- package/dist/ui/theme.d.ts +82 -0
- package/dist/ui/theme.d.ts.map +1 -0
- package/dist/ui/theme.js +142 -0
- package/dist/ui/theme.js.map +1 -0
- package/dist/utils/auth-guard.d.ts +66 -0
- package/dist/utils/auth-guard.d.ts.map +1 -0
- package/dist/utils/auth-guard.js +348 -0
- package/dist/utils/auth-guard.js.map +1 -0
- package/dist/utils/ensure-project.d.ts +11 -0
- package/dist/utils/ensure-project.d.ts.map +1 -0
- package/dist/utils/ensure-project.js +70 -0
- package/dist/utils/ensure-project.js.map +1 -0
- package/dist/utils/git.d.ts +73 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +219 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/github-auth.d.ts +54 -0
- package/dist/utils/github-auth.d.ts.map +1 -0
- package/dist/utils/github-auth.js +375 -0
- package/dist/utils/github-auth.js.map +1 -0
- package/dist/utils/github-repo.d.ts +30 -0
- package/dist/utils/github-repo.d.ts.map +1 -0
- package/dist/utils/github-repo.js +219 -0
- package/dist/utils/github-repo.js.map +1 -0
- package/dist/utils/platform-auth.d.ts +81 -0
- package/dist/utils/platform-auth.d.ts.map +1 -0
- package/dist/utils/platform-auth.js +191 -0
- package/dist/utils/platform-auth.js.map +1 -0
- package/dist/utils/project-config.d.ts +43 -0
- package/dist/utils/project-config.d.ts.map +1 -0
- package/dist/utils/project-config.js +97 -0
- package/dist/utils/project-config.js.map +1 -0
- package/dist/utils/skill-registry.d.ts +49 -0
- package/dist/utils/skill-registry.d.ts.map +1 -0
- package/dist/utils/skill-registry.js +192 -0
- package/dist/utils/skill-registry.js.map +1 -0
- package/dist/utils/wallet.d.ts +62 -0
- package/dist/utils/wallet.d.ts.map +1 -0
- package/dist/utils/wallet.js +252 -0
- package/dist/utils/wallet.js.map +1 -0
- package/dist/utils/x402-client.d.ts +86 -0
- package/dist/utils/x402-client.d.ts.map +1 -0
- package/dist/utils/x402-client.js +265 -0
- package/dist/utils/x402-client.js.map +1 -0
- package/package.json +76 -0
- package/scripts/postinstall.js +116 -0
- package/scripts/test-onboarding.sh +121 -0
- package/scripts/voice-start.sh +128 -0
- package/scripts/voice-stop.sh +33 -0
- package/template/.claude/settings.json +92 -0
- package/template/.claude/skills/agent-browser/SKILL.md +116 -0
- package/template/.claude/skills/brand-architect/SKILL.md +240 -0
- package/template/.claude/skills/brand-architect/config.yaml +137 -0
- package/template/.claude/skills/campaign-hud/config.yaml +112 -0
- package/template/.claude/skills/content-creator/SKILL.md +294 -0
- package/template/.claude/skills/debug/MULTI_AGENT.md +360 -0
- package/template/.claude/skills/debug/SKILL.md +549 -0
- package/template/.claude/skills/fly-deploy/SKILL.md +676 -0
- package/template/.claude/skills/founder-video/SKILL.md +467 -0
- package/template/.claude/skills/hud/SKILL.md +157 -0
- package/template/.claude/skills/ralph-tui/SKILL.md +210 -0
- package/template/.claude/skills/react-best-practices/AGENTS.md +2249 -0
- package/template/.claude/skills/react-best-practices/README.md +123 -0
- package/template/.claude/skills/react-best-practices/SKILL.md +125 -0
- package/template/.claude/skills/react-best-practices/metadata.json +15 -0
- package/template/.claude/skills/react-best-practices/rules/_sections.md +46 -0
- package/template/.claude/skills/react-best-practices/rules/_template.md +28 -0
- package/template/.claude/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/template/.claude/skills/react-best-practices/rules/advanced-use-latest.md +49 -0
- package/template/.claude/skills/react-best-practices/rules/async-api-routes.md +38 -0
- package/template/.claude/skills/react-best-practices/rules/async-defer-await.md +80 -0
- package/template/.claude/skills/react-best-practices/rules/async-dependencies.md +36 -0
- package/template/.claude/skills/react-best-practices/rules/async-parallel.md +28 -0
- package/template/.claude/skills/react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/template/.claude/skills/react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/template/.claude/skills/react-best-practices/rules/bundle-conditional.md +31 -0
- package/template/.claude/skills/react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/template/.claude/skills/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/template/.claude/skills/react-best-practices/rules/bundle-preload.md +50 -0
- package/template/.claude/skills/react-best-practices/rules/client-event-listeners.md +74 -0
- package/template/.claude/skills/react-best-practices/rules/client-swr-dedup.md +56 -0
- package/template/.claude/skills/react-best-practices/rules/js-batch-dom-css.md +82 -0
- package/template/.claude/skills/react-best-practices/rules/js-cache-function-results.md +80 -0
- package/template/.claude/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
- package/template/.claude/skills/react-best-practices/rules/js-cache-storage.md +70 -0
- package/template/.claude/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
- package/template/.claude/skills/react-best-practices/rules/js-early-exit.md +50 -0
- package/template/.claude/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/template/.claude/skills/react-best-practices/rules/js-index-maps.md +37 -0
- package/template/.claude/skills/react-best-practices/rules/js-length-check-first.md +49 -0
- package/template/.claude/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
- package/template/.claude/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/template/.claude/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/template/.claude/skills/react-best-practices/rules/rendering-activity.md +26 -0
- package/template/.claude/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/template/.claude/skills/react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/template/.claude/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/template/.claude/skills/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/template/.claude/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/template/.claude/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/template/.claude/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/template/.claude/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
- package/template/.claude/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
- package/template/.claude/skills/react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/template/.claude/skills/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/template/.claude/skills/react-best-practices/rules/rerender-memo.md +44 -0
- package/template/.claude/skills/react-best-practices/rules/rerender-transitions.md +40 -0
- package/template/.claude/skills/react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/template/.claude/skills/react-best-practices/rules/server-cache-lru.md +41 -0
- package/template/.claude/skills/react-best-practices/rules/server-cache-react.md +26 -0
- package/template/.claude/skills/react-best-practices/rules/server-parallel-fetching.md +79 -0
- package/template/.claude/skills/react-best-practices/rules/server-serialization.md +38 -0
- package/template/.claude/skills/remotion-best-practices/SKILL.md +43 -0
- package/template/.claude/skills/remotion-best-practices/rules/3d.md +86 -0
- package/template/.claude/skills/remotion-best-practices/rules/animations.md +29 -0
- package/template/.claude/skills/remotion-best-practices/rules/assets/charts-bar-chart.tsx +173 -0
- package/template/.claude/skills/remotion-best-practices/rules/assets/text-animations-typewriter.tsx +100 -0
- package/template/.claude/skills/remotion-best-practices/rules/assets/text-animations-word-highlight.tsx +108 -0
- package/template/.claude/skills/remotion-best-practices/rules/assets.md +78 -0
- package/template/.claude/skills/remotion-best-practices/rules/audio.md +172 -0
- package/template/.claude/skills/remotion-best-practices/rules/calculate-metadata.md +104 -0
- package/template/.claude/skills/remotion-best-practices/rules/can-decode.md +75 -0
- package/template/.claude/skills/remotion-best-practices/rules/charts.md +58 -0
- package/template/.claude/skills/remotion-best-practices/rules/compositions.md +146 -0
- package/template/.claude/skills/remotion-best-practices/rules/display-captions.md +126 -0
- package/template/.claude/skills/remotion-best-practices/rules/extract-frames.md +229 -0
- package/template/.claude/skills/remotion-best-practices/rules/fonts.md +152 -0
- package/template/.claude/skills/remotion-best-practices/rules/get-audio-duration.md +58 -0
- package/template/.claude/skills/remotion-best-practices/rules/get-video-dimensions.md +68 -0
- package/template/.claude/skills/remotion-best-practices/rules/get-video-duration.md +58 -0
- package/template/.claude/skills/remotion-best-practices/rules/gifs.md +138 -0
- package/template/.claude/skills/remotion-best-practices/rules/images.md +130 -0
- package/template/.claude/skills/remotion-best-practices/rules/import-srt-captions.md +67 -0
- package/template/.claude/skills/remotion-best-practices/rules/lottie.md +68 -0
- package/template/.claude/skills/remotion-best-practices/rules/measuring-dom-nodes.md +35 -0
- package/template/.claude/skills/remotion-best-practices/rules/measuring-text.md +143 -0
- package/template/.claude/skills/remotion-best-practices/rules/sequencing.md +106 -0
- package/template/.claude/skills/remotion-best-practices/rules/tailwind.md +11 -0
- package/template/.claude/skills/remotion-best-practices/rules/text-animations.md +20 -0
- package/template/.claude/skills/remotion-best-practices/rules/timing.md +179 -0
- package/template/.claude/skills/remotion-best-practices/rules/transcribe-captions.md +19 -0
- package/template/.claude/skills/remotion-best-practices/rules/transitions.md +122 -0
- package/template/.claude/skills/remotion-best-practices/rules/trimming.md +53 -0
- package/template/.claude/skills/remotion-best-practices/rules/videos.md +171 -0
- package/template/.claude/skills/search/SKILL.md +220 -0
- package/template/.claude/skills/spec/SKILL.md +377 -0
- package/template/.claude/skills/startup/SKILL.md +310 -0
- package/template/.claude/skills/web-architect/SKILL.md +309 -0
- package/template/.claude/skills/x-algorithm/SKILL.md +305 -0
- package/template/.jfl/config.json +8 -0
- package/template/.mcp.json +11 -0
- package/template/CLAUDE.md +960 -0
- package/template/content/.gitkeep +0 -0
- package/template/context-hub +3 -0
- package/template/knowledge/BRAND_BRIEF.md +124 -0
- package/template/knowledge/BRAND_DECISIONS.md +168 -0
- package/template/knowledge/NARRATIVE.md +114 -0
- package/template/knowledge/ROADMAP.md +128 -0
- package/template/knowledge/THESIS.md +108 -0
- package/template/knowledge/VISION.md +74 -0
- package/template/knowledge/VOICE_AND_TONE.md +146 -0
- package/template/previews/.gitkeep +0 -0
- package/template/scripts/session/auto-commit.sh +245 -0
- package/template/scripts/session/auto-merge.sh +325 -0
- package/template/scripts/session/jfl-doctor.sh +587 -0
- package/template/scripts/session/session-end.sh +194 -0
- package/template/scripts/session/session-init.sh +163 -0
- package/template/scripts/session/session-sync.sh +167 -0
- package/template/scripts/session/test-context-preservation.sh +160 -0
- package/template/skills/agent-browser/SKILL.md +116 -0
- package/template/skills/brand-architect/SKILL.md +240 -0
- package/template/skills/brand-architect/config.yaml +137 -0
- package/template/skills/campaign-hud/config.yaml +112 -0
- package/template/skills/content-creator/SKILL.md +294 -0
- package/template/skills/debug/MULTI_AGENT.md +360 -0
- package/template/skills/debug/SKILL.md +549 -0
- package/template/skills/fly-deploy/SKILL.md +676 -0
- package/template/skills/founder-video/SKILL.md +467 -0
- package/template/skills/hud/SKILL.md +204 -0
- package/template/skills/ralph-tui/SKILL.md +210 -0
- package/template/skills/react-best-practices/AGENTS.md +2249 -0
- package/template/skills/react-best-practices/README.md +123 -0
- package/template/skills/react-best-practices/SKILL.md +125 -0
- package/template/skills/react-best-practices/metadata.json +15 -0
- package/template/skills/react-best-practices/rules/_sections.md +46 -0
- package/template/skills/react-best-practices/rules/_template.md +28 -0
- package/template/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/template/skills/react-best-practices/rules/advanced-use-latest.md +49 -0
- package/template/skills/react-best-practices/rules/async-api-routes.md +38 -0
- package/template/skills/react-best-practices/rules/async-defer-await.md +80 -0
- package/template/skills/react-best-practices/rules/async-dependencies.md +36 -0
- package/template/skills/react-best-practices/rules/async-parallel.md +28 -0
- package/template/skills/react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/template/skills/react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/template/skills/react-best-practices/rules/bundle-conditional.md +31 -0
- package/template/skills/react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/template/skills/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/template/skills/react-best-practices/rules/bundle-preload.md +50 -0
- package/template/skills/react-best-practices/rules/client-event-listeners.md +74 -0
- package/template/skills/react-best-practices/rules/client-swr-dedup.md +56 -0
- package/template/skills/react-best-practices/rules/js-batch-dom-css.md +82 -0
- package/template/skills/react-best-practices/rules/js-cache-function-results.md +80 -0
- package/template/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
- package/template/skills/react-best-practices/rules/js-cache-storage.md +70 -0
- package/template/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
- package/template/skills/react-best-practices/rules/js-early-exit.md +50 -0
- package/template/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/template/skills/react-best-practices/rules/js-index-maps.md +37 -0
- package/template/skills/react-best-practices/rules/js-length-check-first.md +49 -0
- package/template/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
- package/template/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/template/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/template/skills/react-best-practices/rules/rendering-activity.md +26 -0
- package/template/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/template/skills/react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/template/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/template/skills/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/template/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/template/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/template/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/template/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
- package/template/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
- package/template/skills/react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/template/skills/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/template/skills/react-best-practices/rules/rerender-memo.md +44 -0
- package/template/skills/react-best-practices/rules/rerender-transitions.md +40 -0
- package/template/skills/react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/template/skills/react-best-practices/rules/server-cache-lru.md +41 -0
- package/template/skills/react-best-practices/rules/server-cache-react.md +26 -0
- package/template/skills/react-best-practices/rules/server-parallel-fetching.md +79 -0
- package/template/skills/react-best-practices/rules/server-serialization.md +38 -0
- package/template/skills/remotion-best-practices/SKILL.md +43 -0
- package/template/skills/remotion-best-practices/rules/3d.md +86 -0
- package/template/skills/remotion-best-practices/rules/animations.md +29 -0
- package/template/skills/remotion-best-practices/rules/assets/charts-bar-chart.tsx +173 -0
- package/template/skills/remotion-best-practices/rules/assets/text-animations-typewriter.tsx +100 -0
- package/template/skills/remotion-best-practices/rules/assets/text-animations-word-highlight.tsx +108 -0
- package/template/skills/remotion-best-practices/rules/assets.md +78 -0
- package/template/skills/remotion-best-practices/rules/audio.md +172 -0
- package/template/skills/remotion-best-practices/rules/calculate-metadata.md +104 -0
- package/template/skills/remotion-best-practices/rules/can-decode.md +75 -0
- package/template/skills/remotion-best-practices/rules/charts.md +58 -0
- package/template/skills/remotion-best-practices/rules/compositions.md +146 -0
- package/template/skills/remotion-best-practices/rules/display-captions.md +126 -0
- package/template/skills/remotion-best-practices/rules/extract-frames.md +229 -0
- package/template/skills/remotion-best-practices/rules/fonts.md +152 -0
- package/template/skills/remotion-best-practices/rules/get-audio-duration.md +58 -0
- package/template/skills/remotion-best-practices/rules/get-video-dimensions.md +68 -0
- package/template/skills/remotion-best-practices/rules/get-video-duration.md +58 -0
- package/template/skills/remotion-best-practices/rules/gifs.md +138 -0
- package/template/skills/remotion-best-practices/rules/images.md +130 -0
- package/template/skills/remotion-best-practices/rules/import-srt-captions.md +67 -0
- package/template/skills/remotion-best-practices/rules/lottie.md +68 -0
- package/template/skills/remotion-best-practices/rules/measuring-dom-nodes.md +35 -0
- package/template/skills/remotion-best-practices/rules/measuring-text.md +143 -0
- package/template/skills/remotion-best-practices/rules/sequencing.md +106 -0
- package/template/skills/remotion-best-practices/rules/tailwind.md +11 -0
- package/template/skills/remotion-best-practices/rules/text-animations.md +20 -0
- package/template/skills/remotion-best-practices/rules/timing.md +179 -0
- package/template/skills/remotion-best-practices/rules/transcribe-captions.md +19 -0
- package/template/skills/remotion-best-practices/rules/transitions.md +122 -0
- package/template/skills/remotion-best-practices/rules/trimming.md +53 -0
- package/template/skills/remotion-best-practices/rules/videos.md +171 -0
- package/template/skills/search/SKILL.md +220 -0
- package/template/skills/spec/SKILL.md +377 -0
- package/template/skills/startup/SKILL.md +310 -0
- package/template/skills/web-architect/SKILL.md +309 -0
- package/template/skills/x-algorithm/SKILL.md +305 -0
- package/template/suggestions/.gitkeep +0 -0
- package/template/templates/QUICKSTART_SKILL_TO_PRODUCT.md +242 -0
- package/template/templates/brand/BRAND_BRIEF.md +124 -0
- package/template/templates/brand/BRAND_DECISIONS.md +168 -0
- package/template/templates/brand/BRAND_GUIDELINES.md +251 -0
- package/template/templates/brand/VOICE_AND_TONE.md +146 -0
- package/template/templates/brand/global.css +240 -0
- package/template/templates/collaboration/CONTRIBUTOR.md +74 -0
- package/template/templates/collaboration/CRM.md +97 -0
- package/template/templates/collaboration/TASKS.md +83 -0
- package/template/templates/strategic/NARRATIVE.md +114 -0
- package/template/templates/strategic/ROADMAP.md +128 -0
- package/template/templates/strategic/THESIS.md +108 -0
- package/template/templates/strategic/VISION.md +74 -0
|
@@ -0,0 +1,818 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import ora from "ora";
|
|
3
|
+
import inquirer from "inquirer";
|
|
4
|
+
import Conf from "conf";
|
|
5
|
+
import { execSync } from "child_process";
|
|
6
|
+
import { generateSeedPhrase, seedPhraseToPrivateKey, getAddressFromPrivateKey, checkUsdcBalance, validateSeedPhrase, waitForUsdcBalance, getNetworkName, } from "../utils/wallet.js";
|
|
7
|
+
import { getDayPass, getDayPassTimeRemaining } from "../utils/x402-client.js";
|
|
8
|
+
import { markTeammateJoined } from "../utils/auth-guard.js";
|
|
9
|
+
import { getProjectWallet, setProjectWallet, isJflProject } from "../utils/project-config.js";
|
|
10
|
+
import { registerDevice, pollDeviceStatus, savePlatformAuth, getPlatformToken, getPlatformUser, } from "../utils/platform-auth.js";
|
|
11
|
+
const config = new Conf({ projectName: "jfl" });
|
|
12
|
+
const PLATFORM_URL = process.env.JFL_PLATFORM_URL || "https://jfl.run";
|
|
13
|
+
/**
|
|
14
|
+
* Show account status when already authenticated
|
|
15
|
+
*/
|
|
16
|
+
async function showAccountStatus(walletAddress, platformToken, isInteractive) {
|
|
17
|
+
console.log(chalk.bold("\nš° JFL Account\n"));
|
|
18
|
+
const platformUser = getPlatformUser();
|
|
19
|
+
if (platformUser && platformToken) {
|
|
20
|
+
// Platform account mode
|
|
21
|
+
console.log(chalk.cyan("Platform Account"));
|
|
22
|
+
console.log(chalk.gray(` User: ${platformUser.name || platformUser.email || "Unknown"}`));
|
|
23
|
+
console.log(chalk.gray(` Tier: ${platformUser.tier || "Unknown"}`));
|
|
24
|
+
console.log();
|
|
25
|
+
}
|
|
26
|
+
else if (walletAddress) {
|
|
27
|
+
// x402 wallet mode
|
|
28
|
+
console.log(chalk.cyan("Wallet"));
|
|
29
|
+
console.log(chalk.gray(` Address: ${walletAddress}`));
|
|
30
|
+
console.log(chalk.gray(" Mode: x402 Day Pass ($5/day)"));
|
|
31
|
+
console.log();
|
|
32
|
+
// Check balance
|
|
33
|
+
const spinner = ora("Checking balance...").start();
|
|
34
|
+
try {
|
|
35
|
+
const usdc = await checkUsdcBalance(walletAddress);
|
|
36
|
+
const usdcNum = parseFloat(usdc.formatted);
|
|
37
|
+
spinner.stop();
|
|
38
|
+
console.log(chalk.cyan("Balance"));
|
|
39
|
+
console.log(chalk.gray(` USDC: ${usdcNum >= 5 ? chalk.green("$" + usdcNum.toFixed(2)) : chalk.yellow("$" + usdcNum.toFixed(2))}`));
|
|
40
|
+
// Day pass status
|
|
41
|
+
const dayPass = getDayPass();
|
|
42
|
+
const remaining = getDayPassTimeRemaining();
|
|
43
|
+
if (dayPass && remaining) {
|
|
44
|
+
console.log(chalk.green(` Day Pass: Active (${remaining.hours}h ${remaining.minutes}m remaining)`));
|
|
45
|
+
}
|
|
46
|
+
else if (usdcNum >= 5) {
|
|
47
|
+
console.log(chalk.gray(" Day Pass: Ready to activate"));
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
console.log(chalk.yellow(" Day Pass: Need $5 USDC"));
|
|
51
|
+
}
|
|
52
|
+
console.log();
|
|
53
|
+
// Top up instructions if low
|
|
54
|
+
if (usdcNum < 5) {
|
|
55
|
+
console.log(chalk.yellow("To top up, send USDC to:"));
|
|
56
|
+
console.log(chalk.cyan(` ${walletAddress}`));
|
|
57
|
+
console.log(chalk.dim("(No ETH needed - x402 facilitator covers gas)"));
|
|
58
|
+
console.log();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
spinner.fail("Could not check balance");
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Show options
|
|
66
|
+
if (!isInteractive) {
|
|
67
|
+
console.log(chalk.gray("Options:"));
|
|
68
|
+
console.log(chalk.white(" jfl login --force") + chalk.gray(" Change wallet or re-authenticate"));
|
|
69
|
+
console.log(chalk.white(" jfl login --solo") + chalk.gray(" Upgrade to Solo ($49/mo)"));
|
|
70
|
+
console.log(chalk.white(" jfl login --team") + chalk.gray(" Upgrade to Team ($199/mo)"));
|
|
71
|
+
console.log(chalk.white(" jfl logout") + chalk.gray(" Sign out"));
|
|
72
|
+
console.log();
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
// Interactive menu
|
|
76
|
+
const { action } = await inquirer.prompt([
|
|
77
|
+
{
|
|
78
|
+
type: "list",
|
|
79
|
+
name: "action",
|
|
80
|
+
message: "What do you want to do?",
|
|
81
|
+
choices: [
|
|
82
|
+
{ name: "Nothing, just checking", value: "exit" },
|
|
83
|
+
{ name: "Change wallet", value: "change" },
|
|
84
|
+
{ name: "Upgrade to Solo ($49/mo)", value: "solo" },
|
|
85
|
+
{ name: "Upgrade to Team ($199/mo)", value: "team" },
|
|
86
|
+
{ name: "Log out", value: "logout" },
|
|
87
|
+
],
|
|
88
|
+
},
|
|
89
|
+
]);
|
|
90
|
+
if (action === "exit") {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
else if (action === "logout") {
|
|
94
|
+
// Confirm logout since it clears wallet/seed phrase
|
|
95
|
+
const { confirmLogout } = await inquirer.prompt([
|
|
96
|
+
{
|
|
97
|
+
type: "confirm",
|
|
98
|
+
name: "confirmLogout",
|
|
99
|
+
message: "This will clear your wallet and seed phrase. Are you sure?",
|
|
100
|
+
default: false,
|
|
101
|
+
},
|
|
102
|
+
]);
|
|
103
|
+
if (!confirmLogout) {
|
|
104
|
+
console.log(chalk.gray("Cancelled"));
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
logout();
|
|
108
|
+
console.log(chalk.green("\nā Logged out\n"));
|
|
109
|
+
}
|
|
110
|
+
else if (action === "change") {
|
|
111
|
+
// Re-run login flow
|
|
112
|
+
await loginWithX402(true);
|
|
113
|
+
}
|
|
114
|
+
else if (action === "solo" || action === "team") {
|
|
115
|
+
await loginWithGitHub(action === "team" ? "pro" : "solo", true);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
export async function loginCommand(options = {}) {
|
|
119
|
+
// Check if running in non-TTY mode (e.g., from Claude Code)
|
|
120
|
+
// Use !! to ensure boolean (undefined && undefined = undefined, which would trigger default param)
|
|
121
|
+
const isInteractive = !!(process.stdin.isTTY && process.stdout.isTTY);
|
|
122
|
+
// Determine plan from flags
|
|
123
|
+
let plan;
|
|
124
|
+
if (options.x402)
|
|
125
|
+
plan = "x402";
|
|
126
|
+
else if (options.solo)
|
|
127
|
+
plan = "solo";
|
|
128
|
+
else if (options.team)
|
|
129
|
+
plan = "pro";
|
|
130
|
+
else if (options.free)
|
|
131
|
+
plan = "free";
|
|
132
|
+
else if (options.platform)
|
|
133
|
+
plan = "platform";
|
|
134
|
+
// Check if already logged in (with signing capability)
|
|
135
|
+
const existingToken = config.get("token");
|
|
136
|
+
const existingPlatformToken = getPlatformToken();
|
|
137
|
+
const existingWallet = config.get("x402Address");
|
|
138
|
+
const hasSigningKey = config.get("x402PrivateKey") || config.get("x402SeedPhrase");
|
|
139
|
+
// If already authenticated and no specific plan requested, show account status
|
|
140
|
+
if ((existingToken || existingPlatformToken || (existingWallet && hasSigningKey)) && !options.force && !plan) {
|
|
141
|
+
await showAccountStatus(existingWallet, existingToken || existingPlatformToken, isInteractive);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
// View-only address exists - offer to upgrade
|
|
145
|
+
if (existingWallet && !hasSigningKey && !plan) {
|
|
146
|
+
console.log(chalk.bold("\nš JFL - Authenticate\n"));
|
|
147
|
+
console.log(chalk.yellow(`ā ļø You have a view-only address: ${existingWallet}`));
|
|
148
|
+
console.log(chalk.gray(" View-only can't sign payments. Let's set up a real wallet.\n"));
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
console.log(chalk.bold("\nš JFL - Authenticate\n"));
|
|
152
|
+
}
|
|
153
|
+
// Show authentication options
|
|
154
|
+
console.log(chalk.bold("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā"));
|
|
155
|
+
console.log(chalk.bold("ā Choose your authentication method ā"));
|
|
156
|
+
console.log(chalk.bold("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤"));
|
|
157
|
+
console.log(chalk.bold("ā ā"));
|
|
158
|
+
console.log(chalk.cyan("ā Platform Account") + chalk.white(" (Recommended) ā"));
|
|
159
|
+
console.log(chalk.gray("ā āā Sign in with email or wallet ā"));
|
|
160
|
+
console.log(chalk.gray("ā āā Manage subscriptions on jfl.run ā"));
|
|
161
|
+
console.log(chalk.gray("ā āā Dashboard + Deploy + Analytics ā"));
|
|
162
|
+
console.log(chalk.gray("ā āā Free trial, then flexible pricing ā"));
|
|
163
|
+
console.log(chalk.bold("ā ā"));
|
|
164
|
+
console.log(chalk.cyan("ā Day Pass (x402)") + chalk.white(" $5/day per person ā"));
|
|
165
|
+
console.log(chalk.gray("ā āā Only pay the days you use it ā"));
|
|
166
|
+
console.log(chalk.gray("ā āā AI included (no API key needed) ā"));
|
|
167
|
+
console.log(chalk.gray("ā āā Pay with crypto wallet ā"));
|
|
168
|
+
console.log(chalk.gray("ā āā No platform account needed ā"));
|
|
169
|
+
console.log(chalk.bold("ā ā"));
|
|
170
|
+
console.log(chalk.bold("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā"));
|
|
171
|
+
// If no plan specified and non-interactive, show commands and exit
|
|
172
|
+
if (!plan && !isInteractive) {
|
|
173
|
+
console.log(chalk.yellow("\nRunning non-interactively. Specify an auth method:\n"));
|
|
174
|
+
console.log(chalk.white(" jfl login --platform") + chalk.gray(" Platform account (recommended)"));
|
|
175
|
+
console.log(chalk.white(" jfl login --x402") + chalk.gray(" Day Pass ($5/day, crypto)"));
|
|
176
|
+
console.log(chalk.white(" jfl login --free") + chalk.gray(" Stay on trial"));
|
|
177
|
+
console.log();
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
// If no plan specified, prompt interactively
|
|
181
|
+
if (!plan) {
|
|
182
|
+
const { selectedPlan } = await inquirer.prompt([
|
|
183
|
+
{
|
|
184
|
+
type: "list",
|
|
185
|
+
name: "selectedPlan",
|
|
186
|
+
message: "How do you want to authenticate?",
|
|
187
|
+
choices: [
|
|
188
|
+
{ name: chalk.cyan("Platform Account") + " - Email/wallet, manage on jfl.run (recommended)", value: "platform" },
|
|
189
|
+
{ name: chalk.cyan("Day Pass (x402)") + " - $5/day, pay with crypto", value: "x402" },
|
|
190
|
+
new inquirer.Separator(),
|
|
191
|
+
{ name: chalk.gray("Stay on trial (local only, BYOAI)"), value: "free" },
|
|
192
|
+
],
|
|
193
|
+
},
|
|
194
|
+
]);
|
|
195
|
+
plan = selectedPlan;
|
|
196
|
+
}
|
|
197
|
+
if (plan === "free") {
|
|
198
|
+
console.log(chalk.green("\nā Staying on trial"));
|
|
199
|
+
console.log(chalk.gray("\nTrial includes:"));
|
|
200
|
+
console.log(" ⢠Full JFL toolkit");
|
|
201
|
+
console.log(" ⢠All skills (brand, content, etc)");
|
|
202
|
+
console.log(" ⢠Foundation + brand setup");
|
|
203
|
+
console.log(" ⢠Bring your own AI key");
|
|
204
|
+
console.log(chalk.gray("\nUpgrade anytime with: jfl login"));
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
if (plan === "platform") {
|
|
208
|
+
await loginWithPlatform(isInteractive);
|
|
209
|
+
}
|
|
210
|
+
else if (plan === "x402") {
|
|
211
|
+
await loginWithX402(isInteractive);
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
await loginWithGitHub(plan, isInteractive);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
async function loginWithX402(isInteractive = true) {
|
|
218
|
+
console.log(chalk.cyan("\nš° x402 Wallet Setup\n"));
|
|
219
|
+
// Show network
|
|
220
|
+
const networkName = getNetworkName();
|
|
221
|
+
console.log(chalk.dim(`Network: ${networkName}\n`));
|
|
222
|
+
// Check if wallet already exists
|
|
223
|
+
const existingWallet = config.get("x402Address");
|
|
224
|
+
const hasSigningKey = config.get("x402PrivateKey") || config.get("x402SeedPhrase");
|
|
225
|
+
// If wallet exists with signing capability, just check balance and activate
|
|
226
|
+
if (existingWallet && hasSigningKey) {
|
|
227
|
+
console.log(chalk.green("ā Wallet already configured: ") + chalk.cyan(existingWallet.slice(0, 10) + "..." + existingWallet.slice(-8)));
|
|
228
|
+
console.log();
|
|
229
|
+
// Skip to balance check
|
|
230
|
+
const spinner = ora(`Checking balance on ${networkName}...`).start();
|
|
231
|
+
try {
|
|
232
|
+
const usdc = await checkUsdcBalance(existingWallet);
|
|
233
|
+
const usdcNum = parseFloat(usdc.formatted);
|
|
234
|
+
spinner.succeed("Balance checked!");
|
|
235
|
+
console.log();
|
|
236
|
+
console.log(chalk.gray(`USDC Balance: ${usdcNum >= 5 ? chalk.green("$" + usdcNum.toFixed(2)) : chalk.red("$" + usdcNum.toFixed(2))}`));
|
|
237
|
+
console.log(chalk.dim("(No ETH needed - x402 facilitator covers gas)"));
|
|
238
|
+
console.log();
|
|
239
|
+
if (usdcNum >= 5) {
|
|
240
|
+
console.log(chalk.bold.green("ā
Day Pass ready!"));
|
|
241
|
+
console.log(chalk.gray("$5 will be deducted only on days you use JFL.\n"));
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
console.log(chalk.yellow("ā ļø Need $5 USDC to activate Day Pass"));
|
|
245
|
+
console.log(chalk.bold("\nSend USDC to:"));
|
|
246
|
+
console.log(chalk.cyan(" " + existingWallet));
|
|
247
|
+
console.log();
|
|
248
|
+
}
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
catch (error) {
|
|
252
|
+
spinner.fail("Failed to check balance");
|
|
253
|
+
console.error(chalk.red(error));
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
// Explain how x402 works
|
|
258
|
+
console.log(chalk.bold("How x402 works:"));
|
|
259
|
+
console.log(chalk.gray(" ⢠Your wallet lives on YOUR computer (never sent anywhere)"));
|
|
260
|
+
console.log(chalk.gray(` ⢠You load it with USDC on ${networkName}`));
|
|
261
|
+
console.log(chalk.gray(" ⢠When you use JFL, it signs microtransactions automatically"));
|
|
262
|
+
console.log(chalk.gray(" ⢠$5/day is deducted only on days you actively use it"));
|
|
263
|
+
console.log(chalk.gray(" ⢠$0 on days you don't use it\n"));
|
|
264
|
+
// Non-interactive and no wallet: tell them to run interactively
|
|
265
|
+
if (!isInteractive) {
|
|
266
|
+
console.log(chalk.yellow("No wallet configured yet.\n"));
|
|
267
|
+
console.log(chalk.white("To create a wallet, run in your terminal:"));
|
|
268
|
+
console.log(chalk.cyan(" jfl login --x402\n"));
|
|
269
|
+
console.log(chalk.gray("(Wallet creation requires an interactive terminal for security)\n"));
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
// Ask if they have a wallet or need one created
|
|
273
|
+
const { walletChoice } = await inquirer.prompt([
|
|
274
|
+
{
|
|
275
|
+
type: "list",
|
|
276
|
+
name: "walletChoice",
|
|
277
|
+
message: "Do you have an Ethereum wallet?",
|
|
278
|
+
choices: [
|
|
279
|
+
{ name: chalk.green("Create one for me") + " (recommended)", value: "create" },
|
|
280
|
+
{ name: "I have a seed phrase to import", value: "import_seed" },
|
|
281
|
+
{ name: "I have a private key to import", value: "import_key" },
|
|
282
|
+
{ name: "I just want to use an address (view-only)", value: "address_only" },
|
|
283
|
+
],
|
|
284
|
+
},
|
|
285
|
+
]);
|
|
286
|
+
let address;
|
|
287
|
+
let privateKey;
|
|
288
|
+
if (walletChoice === "create") {
|
|
289
|
+
const result = await createNewWallet();
|
|
290
|
+
address = result.address;
|
|
291
|
+
privateKey = result.privateKey;
|
|
292
|
+
}
|
|
293
|
+
else if (walletChoice === "import_seed") {
|
|
294
|
+
const result = await importFromSeedPhrase();
|
|
295
|
+
address = result.address;
|
|
296
|
+
privateKey = result.privateKey;
|
|
297
|
+
}
|
|
298
|
+
else if (walletChoice === "import_key") {
|
|
299
|
+
const result = await importFromPrivateKey();
|
|
300
|
+
address = result.address;
|
|
301
|
+
privateKey = result.privateKey;
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
// Address only - just for checking balance, can't sign transactions
|
|
305
|
+
const { walletAddress } = await inquirer.prompt([
|
|
306
|
+
{
|
|
307
|
+
type: "input",
|
|
308
|
+
name: "walletAddress",
|
|
309
|
+
message: "Your wallet address (0x...):",
|
|
310
|
+
validate: (input) => {
|
|
311
|
+
if (!/^0x[a-fA-F0-9]{40}$/.test(input)) {
|
|
312
|
+
return "Enter a valid Ethereum address";
|
|
313
|
+
}
|
|
314
|
+
return true;
|
|
315
|
+
},
|
|
316
|
+
},
|
|
317
|
+
]);
|
|
318
|
+
address = walletAddress;
|
|
319
|
+
console.log(chalk.yellow("\nā ļø View-only mode: You won't be able to sign transactions"));
|
|
320
|
+
console.log(chalk.gray(" To enable full functionality, re-run with seed phrase or private key\n"));
|
|
321
|
+
}
|
|
322
|
+
// Check on-chain balance
|
|
323
|
+
const spinner = ora(`Checking balance on ${networkName}...`).start();
|
|
324
|
+
try {
|
|
325
|
+
const usdc = await checkUsdcBalance(address);
|
|
326
|
+
let usdcNum = parseFloat(usdc.formatted);
|
|
327
|
+
spinner.succeed("Balance checked!");
|
|
328
|
+
// Display balance
|
|
329
|
+
console.log();
|
|
330
|
+
console.log(chalk.gray(`Address: ${chalk.cyan(address.slice(0, 10) + "..." + address.slice(-8))}`));
|
|
331
|
+
console.log(chalk.gray(`USDC Balance: ${usdcNum >= 5 ? chalk.green("$" + usdcNum.toFixed(2)) : chalk.red("$" + usdcNum.toFixed(2))}`));
|
|
332
|
+
console.log(chalk.dim("(No ETH needed - x402 facilitator covers gas)"));
|
|
333
|
+
console.log();
|
|
334
|
+
// Save wallet to config first (so it persists even if they close)
|
|
335
|
+
config.set("x402Address", address);
|
|
336
|
+
config.set("authMethod", "x402");
|
|
337
|
+
if (privateKey) {
|
|
338
|
+
// Store encrypted private key (in production, use proper encryption)
|
|
339
|
+
config.set("x402PrivateKey", privateKey);
|
|
340
|
+
}
|
|
341
|
+
config.delete("token"); // Clear platform token if any
|
|
342
|
+
// Check if they have enough
|
|
343
|
+
if (usdcNum < 5) {
|
|
344
|
+
console.log(chalk.yellow("ā ļø Need $5 USDC to activate Day Pass"));
|
|
345
|
+
console.log();
|
|
346
|
+
console.log(chalk.bold("Send USDC to:"));
|
|
347
|
+
console.log();
|
|
348
|
+
console.log(chalk.cyan("ā" + "ā".repeat(44) + "ā"));
|
|
349
|
+
console.log(chalk.cyan("ā") + " " + chalk.bold.white(address) + " " + chalk.cyan("ā"));
|
|
350
|
+
console.log(chalk.cyan("ā" + "ā".repeat(44) + "ā"));
|
|
351
|
+
console.log();
|
|
352
|
+
console.log(chalk.dim("Tip: Use Coinbase, Bridge, or any exchange that supports Base"));
|
|
353
|
+
console.log();
|
|
354
|
+
console.log(chalk.dim("Press q or Escape to exit and fund later"));
|
|
355
|
+
console.log();
|
|
356
|
+
// Wait for funding
|
|
357
|
+
const fundingSpinner = ora("Waiting for $5 USDC...").start();
|
|
358
|
+
const funded = await waitForUsdcBalance(address, 5, (balance) => {
|
|
359
|
+
fundingSpinner.text = `Balance: $${balance.toFixed(2)} USDC (need $5.00) - press q to exit`;
|
|
360
|
+
});
|
|
361
|
+
if (!funded) {
|
|
362
|
+
fundingSpinner.info("Exited - wallet saved");
|
|
363
|
+
console.log();
|
|
364
|
+
console.log(chalk.gray("Your wallet is saved. When you fund it with $5 USDC,"));
|
|
365
|
+
console.log(chalk.gray("JFL will automatically activate your Day Pass."));
|
|
366
|
+
console.log();
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
fundingSpinner.succeed("Funded!");
|
|
370
|
+
console.log();
|
|
371
|
+
// Re-check balances
|
|
372
|
+
const newUsdc = await checkUsdcBalance(address);
|
|
373
|
+
usdcNum = parseFloat(newUsdc.formatted);
|
|
374
|
+
}
|
|
375
|
+
// Success!
|
|
376
|
+
console.log(chalk.bold.green("ā
Wallet ready!"));
|
|
377
|
+
console.log();
|
|
378
|
+
console.log(chalk.gray("You now have access to:"));
|
|
379
|
+
console.log(" ⢠AI included (no API key needed)");
|
|
380
|
+
console.log(" ⢠Chat in Telegram, Slack, Discord");
|
|
381
|
+
console.log(" ⢠Dashboard + Deploy at jfl.run");
|
|
382
|
+
console.log(" ⢠Parallel agents");
|
|
383
|
+
console.log();
|
|
384
|
+
console.log(chalk.gray("$5 will be deducted only on days you use JFL."));
|
|
385
|
+
console.log(chalk.gray("$0 on days you don't use it."));
|
|
386
|
+
console.log();
|
|
387
|
+
// If in a JFL project, set this as the project wallet if not already set
|
|
388
|
+
await handleProjectWallet(address);
|
|
389
|
+
// Check if user is joining as a teammate
|
|
390
|
+
await handleTeammateJoin();
|
|
391
|
+
}
|
|
392
|
+
catch (error) {
|
|
393
|
+
spinner.fail("Failed to check balance");
|
|
394
|
+
console.error(chalk.red(error));
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
async function createNewWallet() {
|
|
398
|
+
console.log(chalk.bold("\nš Creating your wallet...\n"));
|
|
399
|
+
// Security disclosure
|
|
400
|
+
console.log(chalk.bold.cyan("Security & Privacy"));
|
|
401
|
+
console.log(chalk.gray("ā".repeat(60)));
|
|
402
|
+
console.log();
|
|
403
|
+
console.log(chalk.white(" ⢠Your seed phrase is generated locally on THIS computer"));
|
|
404
|
+
console.log(chalk.white(" ⢠It will NEVER be sent to any server or third party"));
|
|
405
|
+
console.log(chalk.white(" ⢠All signing happens locally on your device"));
|
|
406
|
+
console.log(chalk.white(" ⢠You are in full control of your funds"));
|
|
407
|
+
console.log();
|
|
408
|
+
console.log(chalk.white(" ⢠Your seed phrase will be stored encrypted at:"));
|
|
409
|
+
console.log(chalk.cyan(` ${config.path}`));
|
|
410
|
+
console.log();
|
|
411
|
+
console.log(chalk.bold.red("ā ļø If you lose your seed phrase, you lose access to your wallet"));
|
|
412
|
+
console.log(chalk.bold.red(" There is no recovery. Write it down and store it safely."));
|
|
413
|
+
console.log();
|
|
414
|
+
const { understood } = await inquirer.prompt([
|
|
415
|
+
{
|
|
416
|
+
type: "confirm",
|
|
417
|
+
name: "understood",
|
|
418
|
+
message: "I understand. Generate my wallet.",
|
|
419
|
+
default: false,
|
|
420
|
+
},
|
|
421
|
+
]);
|
|
422
|
+
if (!understood) {
|
|
423
|
+
throw new Error("Wallet creation cancelled");
|
|
424
|
+
}
|
|
425
|
+
// Generate seed phrase
|
|
426
|
+
const seedPhrase = generateSeedPhrase(12);
|
|
427
|
+
const privateKey = seedPhraseToPrivateKey(seedPhrase, 0);
|
|
428
|
+
const address = getAddressFromPrivateKey(privateKey);
|
|
429
|
+
// Display seed phrase prominently
|
|
430
|
+
console.log();
|
|
431
|
+
console.log(chalk.bold.yellow("š YOUR SEED PHRASE - WRITE THIS DOWN"));
|
|
432
|
+
console.log();
|
|
433
|
+
console.log(chalk.cyan("ā" + "ā".repeat(58) + "ā"));
|
|
434
|
+
const words = seedPhrase.split(" ");
|
|
435
|
+
for (let i = 0; i < words.length; i += 3) {
|
|
436
|
+
const row = words.slice(i, i + 3);
|
|
437
|
+
const formatted = row.map((word, j) => {
|
|
438
|
+
const num = (i + j + 1).toString().padStart(2, " ");
|
|
439
|
+
return `${chalk.dim(num + ".")} ${chalk.bold.white(word.padEnd(12))}`;
|
|
440
|
+
}).join(" ");
|
|
441
|
+
console.log(chalk.cyan("ā") + " " + formatted + " " + chalk.cyan("ā"));
|
|
442
|
+
}
|
|
443
|
+
console.log(chalk.cyan("ā" + "ā".repeat(58) + "ā"));
|
|
444
|
+
console.log();
|
|
445
|
+
console.log(chalk.dim("Copy this (select and copy the line below):"));
|
|
446
|
+
console.log(chalk.white.bold(words.join(" ")));
|
|
447
|
+
console.log();
|
|
448
|
+
// Verify they saved it by asking for 3 words
|
|
449
|
+
console.log(chalk.yellow("To confirm you saved it, enter these 3 words:\n"));
|
|
450
|
+
const verifyIndices = [0, 5, 11]; // Words #1, #6, #12
|
|
451
|
+
for (const idx of verifyIndices) {
|
|
452
|
+
const { word } = await inquirer.prompt([
|
|
453
|
+
{
|
|
454
|
+
type: "input",
|
|
455
|
+
name: "word",
|
|
456
|
+
message: `Word #${idx + 1}:`,
|
|
457
|
+
},
|
|
458
|
+
]);
|
|
459
|
+
if (word.trim().toLowerCase() !== words[idx].toLowerCase()) {
|
|
460
|
+
console.log(chalk.red("\nā Incorrect. Please write down your seed phrase and try again."));
|
|
461
|
+
throw new Error("Seed phrase verification failed");
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
console.log(chalk.green("\nā Seed phrase verified!"));
|
|
465
|
+
// Display wallet address
|
|
466
|
+
console.log();
|
|
467
|
+
console.log(chalk.bold.green("⨠Wallet created!"));
|
|
468
|
+
console.log();
|
|
469
|
+
console.log(chalk.bold("Your wallet address:"));
|
|
470
|
+
console.log(chalk.cyan("ā" + "ā".repeat(44) + "ā"));
|
|
471
|
+
console.log(chalk.cyan("ā") + " " + chalk.bold.white(address) + " " + chalk.cyan("ā"));
|
|
472
|
+
console.log(chalk.cyan("ā" + "ā".repeat(44) + "ā"));
|
|
473
|
+
console.log();
|
|
474
|
+
// Save seed phrase
|
|
475
|
+
config.set("x402SeedPhrase", seedPhrase);
|
|
476
|
+
console.log(chalk.dim("š¾ Config saved to: ") + chalk.cyan(config.path));
|
|
477
|
+
console.log();
|
|
478
|
+
return { address, privateKey };
|
|
479
|
+
}
|
|
480
|
+
async function importFromSeedPhrase() {
|
|
481
|
+
console.log(chalk.bold("\nš Import from Seed Phrase\n"));
|
|
482
|
+
const { seedPhrase } = await inquirer.prompt([
|
|
483
|
+
{
|
|
484
|
+
type: "password",
|
|
485
|
+
name: "seedPhrase",
|
|
486
|
+
message: "Enter your 12 or 24 word seed phrase:",
|
|
487
|
+
mask: "ā¢",
|
|
488
|
+
validate: (input) => {
|
|
489
|
+
const words = input.trim().split(/\s+/);
|
|
490
|
+
if (words.length !== 12 && words.length !== 24) {
|
|
491
|
+
return "Seed phrase must be 12 or 24 words";
|
|
492
|
+
}
|
|
493
|
+
if (!validateSeedPhrase(input.trim())) {
|
|
494
|
+
return "Invalid seed phrase";
|
|
495
|
+
}
|
|
496
|
+
return true;
|
|
497
|
+
},
|
|
498
|
+
},
|
|
499
|
+
]);
|
|
500
|
+
const privateKey = seedPhraseToPrivateKey(seedPhrase.trim(), 0);
|
|
501
|
+
const address = getAddressFromPrivateKey(privateKey);
|
|
502
|
+
console.log(chalk.green("\nā Seed phrase imported"));
|
|
503
|
+
console.log(chalk.gray(` Address: ${address}\n`));
|
|
504
|
+
// Save seed phrase
|
|
505
|
+
config.set("x402SeedPhrase", seedPhrase.trim());
|
|
506
|
+
return { address, privateKey };
|
|
507
|
+
}
|
|
508
|
+
async function importFromPrivateKey() {
|
|
509
|
+
console.log(chalk.bold("\nš Import from Private Key\n"));
|
|
510
|
+
const { privateKeyInput } = await inquirer.prompt([
|
|
511
|
+
{
|
|
512
|
+
type: "password",
|
|
513
|
+
name: "privateKeyInput",
|
|
514
|
+
message: "Enter your private key (0x...):",
|
|
515
|
+
mask: "ā¢",
|
|
516
|
+
validate: (input) => {
|
|
517
|
+
if (!input.startsWith("0x") || input.length !== 66) {
|
|
518
|
+
return "Private key must start with 0x and be 66 characters";
|
|
519
|
+
}
|
|
520
|
+
return true;
|
|
521
|
+
},
|
|
522
|
+
},
|
|
523
|
+
]);
|
|
524
|
+
const privateKey = privateKeyInput;
|
|
525
|
+
const address = getAddressFromPrivateKey(privateKey);
|
|
526
|
+
console.log(chalk.green("\nā Private key imported"));
|
|
527
|
+
console.log(chalk.gray(` Address: ${address}\n`));
|
|
528
|
+
return { address, privateKey };
|
|
529
|
+
}
|
|
530
|
+
async function loginWithGitHub(plan = "solo", isInteractive = true) {
|
|
531
|
+
const planName = plan === "pro" ? "Team ($199/mo)" : "Solo ($49/mo)";
|
|
532
|
+
console.log(chalk.cyan(`\nš GitHub Authentication - ${planName}\n`));
|
|
533
|
+
const loginUrl = `${PLATFORM_URL}/login?cli=true&plan=${plan}`;
|
|
534
|
+
// Non-interactive: just show URL
|
|
535
|
+
if (!isInteractive) {
|
|
536
|
+
console.log(chalk.yellow("Running non-interactively.\n"));
|
|
537
|
+
console.log(chalk.white("To authenticate:"));
|
|
538
|
+
console.log(chalk.gray(" 1. Open this URL in your browser:"));
|
|
539
|
+
console.log(chalk.cyan(` ${loginUrl}`));
|
|
540
|
+
console.log(chalk.gray(" 2. Sign in with GitHub"));
|
|
541
|
+
console.log(chalk.gray(" 3. Copy the API token"));
|
|
542
|
+
console.log(chalk.gray(" 4. Run: jfl login --token <your-token>\n"));
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
console.log(chalk.gray("Opening browser for GitHub login...\n"));
|
|
546
|
+
try {
|
|
547
|
+
// Open browser
|
|
548
|
+
const openCommand = process.platform === "darwin"
|
|
549
|
+
? "open"
|
|
550
|
+
: process.platform === "win32"
|
|
551
|
+
? "start"
|
|
552
|
+
: "xdg-open";
|
|
553
|
+
execSync(`${openCommand} "${loginUrl}"`, { stdio: "pipe" });
|
|
554
|
+
}
|
|
555
|
+
catch {
|
|
556
|
+
console.log(chalk.yellow("Could not open browser automatically."));
|
|
557
|
+
console.log(chalk.gray(`Please visit: ${loginUrl}`));
|
|
558
|
+
}
|
|
559
|
+
console.log(chalk.cyan("Waiting for authentication...\n"));
|
|
560
|
+
// For production, use device code flow
|
|
561
|
+
// For now, ask for token directly
|
|
562
|
+
const { token } = await inquirer.prompt([
|
|
563
|
+
{
|
|
564
|
+
type: "password",
|
|
565
|
+
name: "token",
|
|
566
|
+
message: "Paste your API token from the browser:",
|
|
567
|
+
mask: "*",
|
|
568
|
+
},
|
|
569
|
+
]);
|
|
570
|
+
if (!token) {
|
|
571
|
+
console.log(chalk.red("No token provided."));
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
const spinner = ora("Verifying...").start();
|
|
575
|
+
try {
|
|
576
|
+
const res = await fetch(`${PLATFORM_URL}/api/auth/verify`, {
|
|
577
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
578
|
+
});
|
|
579
|
+
if (!res.ok) {
|
|
580
|
+
spinner.fail("Invalid token");
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
const user = await res.json();
|
|
584
|
+
// Save token
|
|
585
|
+
config.set("token", token);
|
|
586
|
+
config.set("user", user);
|
|
587
|
+
config.set("authMethod", "github");
|
|
588
|
+
config.delete("x402Address"); // Clear x402 if any
|
|
589
|
+
spinner.succeed(`Logged in as ${user.name || user.email}`);
|
|
590
|
+
console.log(chalk.bold.green("\nā
Authenticated!\n"));
|
|
591
|
+
console.log(chalk.gray(`Tier: ${user.tier}`));
|
|
592
|
+
if (user.tier === "FREE" || user.tier === "TRIAL") {
|
|
593
|
+
console.log(chalk.yellow("\nš” You're on trial. Pay when you get value:"));
|
|
594
|
+
console.log(" x402 ($5/day) - Per person, pay as you go");
|
|
595
|
+
console.log(" Solo ($49/mo) - Just you, fixed monthly");
|
|
596
|
+
console.log(" Pro ($199/mo) - Team up to 5 (+$25/seat)");
|
|
597
|
+
console.log(chalk.gray(`\n Visit: ${PLATFORM_URL}/dashboard/settings`));
|
|
598
|
+
}
|
|
599
|
+
else if (user.tier === "SOLO") {
|
|
600
|
+
console.log(chalk.green("\nā Solo - AI included, Dashboard, Deploy"));
|
|
601
|
+
}
|
|
602
|
+
else if (user.tier === "PRO") {
|
|
603
|
+
console.log(chalk.green("\nā Pro - Team, Parallel agents, Analytics"));
|
|
604
|
+
}
|
|
605
|
+
else if (user.tier === "X402") {
|
|
606
|
+
console.log(chalk.green("\nā x402 - Pay as you go ($5/day active)"));
|
|
607
|
+
}
|
|
608
|
+
console.log();
|
|
609
|
+
// Check if user is joining as a teammate
|
|
610
|
+
await handleTeammateJoin();
|
|
611
|
+
}
|
|
612
|
+
catch (error) {
|
|
613
|
+
spinner.fail("Authentication failed");
|
|
614
|
+
console.error(chalk.red(error));
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
async function loginWithPlatform(isInteractive = true) {
|
|
618
|
+
console.log(chalk.cyan("\nš Platform Authentication\n"));
|
|
619
|
+
// Non-interactive: show instructions
|
|
620
|
+
if (!isInteractive) {
|
|
621
|
+
console.log(chalk.yellow("Running non-interactively.\n"));
|
|
622
|
+
console.log(chalk.white("To authenticate with the platform:"));
|
|
623
|
+
console.log(chalk.gray(" Run this command in your terminal:"));
|
|
624
|
+
console.log(chalk.cyan(" jfl login --platform\n"));
|
|
625
|
+
console.log(chalk.gray("(Platform login requires an interactive terminal)\n"));
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
try {
|
|
629
|
+
// Step 1: Register device and get code
|
|
630
|
+
console.log(chalk.gray("Registering device...\n"));
|
|
631
|
+
const { deviceId, deviceCode, verificationUrl, expiresIn } = await registerDevice();
|
|
632
|
+
// Step 2: Show code to user
|
|
633
|
+
console.log(chalk.bold("š Device Code\n"));
|
|
634
|
+
console.log(chalk.cyan("āāāāāāāāāāāāāā"));
|
|
635
|
+
console.log(chalk.cyan("ā ") + chalk.bold.white(deviceCode) + chalk.cyan(" ā"));
|
|
636
|
+
console.log(chalk.cyan("āāāāāāāāāāāāāā"));
|
|
637
|
+
console.log();
|
|
638
|
+
console.log(chalk.gray("1. Opening browser to link your device..."));
|
|
639
|
+
console.log(chalk.gray(`2. Sign in with your email or wallet`));
|
|
640
|
+
console.log(chalk.gray(`3. Enter the code: ${chalk.yellow(deviceCode)}`));
|
|
641
|
+
console.log();
|
|
642
|
+
console.log(chalk.dim(`Code expires in ${expiresIn} seconds`));
|
|
643
|
+
console.log();
|
|
644
|
+
// Step 3: Open browser
|
|
645
|
+
try {
|
|
646
|
+
const openCommand = process.platform === "darwin"
|
|
647
|
+
? "open"
|
|
648
|
+
: process.platform === "win32"
|
|
649
|
+
? "start"
|
|
650
|
+
: "xdg-open";
|
|
651
|
+
execSync(`${openCommand} "${verificationUrl}"`, { stdio: "pipe" });
|
|
652
|
+
}
|
|
653
|
+
catch {
|
|
654
|
+
console.log(chalk.yellow("Could not open browser automatically."));
|
|
655
|
+
console.log(chalk.gray(`Please visit: ${verificationUrl}`));
|
|
656
|
+
console.log();
|
|
657
|
+
}
|
|
658
|
+
// Step 4: Poll for linking
|
|
659
|
+
const spinner = ora("Waiting for authentication...").start();
|
|
660
|
+
const result = await pollDeviceStatus(deviceId, expiresIn, spinner);
|
|
661
|
+
if (!result.success || !result.jwt || !result.user) {
|
|
662
|
+
if (result.reason === 'expired') {
|
|
663
|
+
spinner.fail("Device code expired (5 minute limit)");
|
|
664
|
+
console.log(chalk.yellow("\nThe device code has expired. Please try again:\n"));
|
|
665
|
+
}
|
|
666
|
+
else {
|
|
667
|
+
spinner.fail("Authentication timed out");
|
|
668
|
+
console.log(chalk.yellow("\nNo response received. Please try again:\n"));
|
|
669
|
+
}
|
|
670
|
+
console.log(chalk.cyan(" jfl login --platform\n"));
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
673
|
+
// Step 5: Save authentication
|
|
674
|
+
savePlatformAuth(result.jwt, result.user);
|
|
675
|
+
spinner.succeed(`Authenticated as ${chalk.green(result.user.name || result.user.email)}`);
|
|
676
|
+
console.log(chalk.bold.green("\nā
Platform account linked!\n"));
|
|
677
|
+
console.log(chalk.gray(`User: ${result.user.name || result.user.email}`));
|
|
678
|
+
console.log(chalk.gray(`Tier: ${result.user.tier || "Free"}`));
|
|
679
|
+
console.log();
|
|
680
|
+
if (result.user.tier === "FREE" || !result.user.tier) {
|
|
681
|
+
console.log(chalk.cyan("š You're on the free trial!"));
|
|
682
|
+
console.log(chalk.gray("\nUpgrade anytime at: ") + chalk.cyan(`${PLATFORM_URL}/dashboard/settings`));
|
|
683
|
+
}
|
|
684
|
+
console.log();
|
|
685
|
+
// Check if user is joining as a teammate
|
|
686
|
+
await handleTeammateJoin();
|
|
687
|
+
}
|
|
688
|
+
catch (error) {
|
|
689
|
+
console.error(chalk.red("\nā Authentication failed"));
|
|
690
|
+
console.error(chalk.gray(error instanceof Error ? error.message : String(error)));
|
|
691
|
+
console.log();
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
// ============================================================================
|
|
695
|
+
// PROJECT WALLET
|
|
696
|
+
// ============================================================================
|
|
697
|
+
/**
|
|
698
|
+
* Set up project wallet if in a JFL project
|
|
699
|
+
* Called after successful wallet setup
|
|
700
|
+
*/
|
|
701
|
+
async function handleProjectWallet(walletAddress) {
|
|
702
|
+
// Check if we're in a JFL project
|
|
703
|
+
if (!isJflProject())
|
|
704
|
+
return;
|
|
705
|
+
// Check if project already has a wallet
|
|
706
|
+
const existingWallet = getProjectWallet();
|
|
707
|
+
if (existingWallet) {
|
|
708
|
+
// Project already has a wallet - check if it's this one
|
|
709
|
+
if (existingWallet.toLowerCase() === walletAddress.toLowerCase()) {
|
|
710
|
+
console.log(chalk.green("ā This wallet is already the project wallet"));
|
|
711
|
+
}
|
|
712
|
+
else {
|
|
713
|
+
console.log(chalk.gray(`Project wallet: ${existingWallet.slice(0, 10)}...${existingWallet.slice(-8)}`));
|
|
714
|
+
console.log(chalk.gray("(Different from your wallet - you're a contributor)"));
|
|
715
|
+
}
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
// No project wallet yet - get username and set it
|
|
719
|
+
let username = "owner";
|
|
720
|
+
try {
|
|
721
|
+
username = execSync("git config user.name", { encoding: "utf-8" }).trim();
|
|
722
|
+
}
|
|
723
|
+
catch {
|
|
724
|
+
// Use default
|
|
725
|
+
}
|
|
726
|
+
// Set this as the project wallet
|
|
727
|
+
setProjectWallet(walletAddress, username);
|
|
728
|
+
console.log(chalk.green("ā Set as project wallet"));
|
|
729
|
+
console.log(chalk.gray(" Teammates can now contribute to this project"));
|
|
730
|
+
}
|
|
731
|
+
// ============================================================================
|
|
732
|
+
// TEAMMATE JOINING
|
|
733
|
+
// ============================================================================
|
|
734
|
+
/**
|
|
735
|
+
* Check if current user is a teammate (has suggestions file) and mark them as joined
|
|
736
|
+
* Called after successful authentication
|
|
737
|
+
*/
|
|
738
|
+
async function handleTeammateJoin() {
|
|
739
|
+
const cwd = process.cwd();
|
|
740
|
+
const suggestionsDir = `${cwd}/suggestions`;
|
|
741
|
+
try {
|
|
742
|
+
const { existsSync, readdirSync } = require('fs');
|
|
743
|
+
if (!existsSync(suggestionsDir))
|
|
744
|
+
return;
|
|
745
|
+
// Get username from git config
|
|
746
|
+
let username;
|
|
747
|
+
try {
|
|
748
|
+
username = execSync('git config user.name', { encoding: 'utf-8' }).trim().toLowerCase();
|
|
749
|
+
}
|
|
750
|
+
catch {
|
|
751
|
+
return; // Can't determine username
|
|
752
|
+
}
|
|
753
|
+
if (!username)
|
|
754
|
+
return;
|
|
755
|
+
// Check for matching suggestions file
|
|
756
|
+
const files = readdirSync(suggestionsDir);
|
|
757
|
+
const matchingFile = files.find((f) => {
|
|
758
|
+
const name = f.replace('.md', '').toLowerCase();
|
|
759
|
+
return name === username || name.includes(username);
|
|
760
|
+
});
|
|
761
|
+
if (matchingFile) {
|
|
762
|
+
const teammateUsername = matchingFile.replace('.md', '');
|
|
763
|
+
const marked = markTeammateJoined(teammateUsername);
|
|
764
|
+
if (marked) {
|
|
765
|
+
console.log(chalk.green(`\nā Joined project as teammate: ${teammateUsername}`));
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
catch {
|
|
770
|
+
// Silent fail - not critical
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
// ============================================================================
|
|
774
|
+
// EXPORTS
|
|
775
|
+
// ============================================================================
|
|
776
|
+
export function getToken() {
|
|
777
|
+
return config.get("token");
|
|
778
|
+
}
|
|
779
|
+
export function getX402Address() {
|
|
780
|
+
return config.get("x402Address");
|
|
781
|
+
}
|
|
782
|
+
export function getAuthMethod() {
|
|
783
|
+
return config.get("authMethod");
|
|
784
|
+
}
|
|
785
|
+
export function getUser() {
|
|
786
|
+
// Check for platform user first (newer auth method)
|
|
787
|
+
const platformUser = getPlatformUser();
|
|
788
|
+
if (platformUser) {
|
|
789
|
+
return {
|
|
790
|
+
id: platformUser.id,
|
|
791
|
+
name: platformUser.name,
|
|
792
|
+
email: platformUser.email,
|
|
793
|
+
tier: platformUser.tier,
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
// Fall back to legacy GitHub auth user
|
|
797
|
+
return config.get("user");
|
|
798
|
+
}
|
|
799
|
+
export function isAuthenticated() {
|
|
800
|
+
// For x402, need signing capability (not just view-only address)
|
|
801
|
+
const hasX402Signing = getX402Address() && (config.get("x402PrivateKey") || config.get("x402SeedPhrase"));
|
|
802
|
+
const hasPlatformAuth = getPlatformToken();
|
|
803
|
+
return !!(getToken() || hasX402Signing || hasPlatformAuth);
|
|
804
|
+
}
|
|
805
|
+
export function isViewOnly() {
|
|
806
|
+
return !!(getX402Address() && !config.get("x402PrivateKey") && !config.get("x402SeedPhrase"));
|
|
807
|
+
}
|
|
808
|
+
export function logout() {
|
|
809
|
+
config.delete("token");
|
|
810
|
+
config.delete("user");
|
|
811
|
+
config.delete("x402Address");
|
|
812
|
+
config.delete("x402PrivateKey");
|
|
813
|
+
config.delete("x402SeedPhrase");
|
|
814
|
+
config.delete("platformToken");
|
|
815
|
+
config.delete("platformUser");
|
|
816
|
+
config.delete("authMethod");
|
|
817
|
+
}
|
|
818
|
+
//# sourceMappingURL=login.js.map
|