Compare commits

...

4 Commits

Author SHA1 Message Date
posimai b0e77839c2 chore: add src-tauri/target to .gitignore (prevent large build artifacts) 2026-04-14 23:30:31 +09:00
posimai 5bcd60eefd chore: update STATUS.md — ponshu APK + guard-ext + VS Code fix done 2026-04-14 23:22:25 +09:00
posimai b25b3f640e fix(brain): switch analyzeWithGemini from gemini-2.0-flash-lite to gemini-2.5-flash
gemini-2.0-flash-lite のフリー枠日次クォータが枯渇し AI 分析が全件失敗していた。
gemini-2.5-flash に統一してクォータ問題を解消する。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 23:12:50 +09:00
posimai e7594370e7 feat(ext): integrate rule engine — scan works without API key
- runRuleEngine() now runs first on every scan (133 rules, instant)
- Gemini/Claude become optional enhancement layers (deeper semantic scan)
- Removed hard requirement for API key to run scanWorkspace/scanFile
- tsconfig: remove rootDir restriction to allow relative import from guard app
- Status bar tooltip updated to reflect API-key-free scanning
- Bundle: 120kb minified (ruleEngine + RULE_FIXES included via esbuild)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 23:05:19 +09:00
111 changed files with 23302 additions and 104 deletions

15
.claude/settings.json Normal file
View File

@ -0,0 +1,15 @@
{
"permissions": {
"allow": [
"Bash(for dir in posimai-brain posimai-feed posimai-reader posimai-events posimai-hotels posimai-maps posimai-together posimai-analytics)",
"Bash(do echo \"=== $dir ===\")",
"Read(//c/Users/maita/posimai-project/$dir/**)",
"Bash(done)",
"Bash(for dir in posimai-brain posimai-feed posimai-events posimai-hotels posimai-maps)",
"Read(//c/Users/maita/posimai-project/**)",
"Bash(grep -E \"\\\\.html$|next\\\\.config|tsconfig\")",
"Bash(vercel --version)",
"Bash(vercel whoami)"
]
}
}

View File

@ -0,0 +1,34 @@
{
"permissions": {
"allow": [
"Bash(ssh mai@100.76.7.3 'sed -n \"\"28,35p\"\" /volume1/docker/posimai_lab/posimai-api/server.js')",
"Bash(ssh mai@100.76.7.3 'sed -i \"\"30s/.*/ if (!origin || allowedOrigins.includes(origin) || (origin \\&\\& origin.match(\\/^https:\\\\\\\\\\\\/\\\\\\\\\\\\/.*\\\\\\\\.vercel\\\\\\\\.app$\\/))) cb(null, true);/\"\" /volume1/docker/posimai_lab/posimai-api/server.js && sed -n \"\"28,35p\"\" /volume1/docker/posimai_lab/posimai-api/server.js')",
"Bash(ssh mai@100.76.7.3 \"perl -i.bak -pe ''s/if \\(!origin \\|\\| allowedOrigins\\.includes\\(origin\\)\\) cb\\(null, true\\);/if (!origin || allowedOrigins.includes(origin) || (origin \\&\\& \\/^https:\\\\\\/\\\\\\/.*\\\\.vercel\\\\.app$\\/.test(origin))) cb(null, true);/'' /volume1/docker/posimai_lab/posimai-api/server.js && sed -n ''28,35p'' /volume1/docker/posimai_lab/posimai-api/server.js\")",
"Bash(ssh mai@100.76.7.3 \"head -29 /volume1/docker/posimai_lab/posimai-api/server.js > /tmp/server-part1.js && echo '' if (!origin || allowedOrigins.includes(origin) || (origin && /^https:\\/\\/.*\\.vercel\\.app$/.test(origin))) cb(null, true);'' > /tmp/server-part2.js && tail -n +31 /volume1/docker/posimai_lab/posimai-api/server.js > /tmp/server-part3.js && cat /tmp/server-part1.js /tmp/server-part2.js /tmp/server-part3.js > /volume1/docker/posimai_lab/posimai-api/server.js && sed -n ''28,35p'' /volume1/docker/posimai_lab/posimai-api/server.js\")",
"Bash(ssh mai@100.76.7.3 \"cp /volume1/docker/posimai_lab/posimai-api/server.js.backup-cors /volume1/docker/posimai_lab/posimai-api/server.js && head -29 /volume1/docker/posimai_lab/posimai-api/server.js > /tmp/server-new.js && echo '' if (!origin || allowedOrigins.includes(origin) || (origin && /^https:\\/\\/.*\\.vercel\\.app$/.test(origin))) cb(null, true);'' >> /tmp/server-new.js && tail -n +31 /volume1/docker/posimai_lab/posimai-api/server.js >> /tmp/server-new.js && mv /tmp/server-new.js /volume1/docker/posimai_lab/posimai-api/server.js && echo ''Updated CORS:'' && sed -n ''28,33p'' /volume1/docker/posimai_lab/posimai-api/server.js\")",
"Bash(ssh mai@100.76.7.3 \"cp /volume1/docker/posimai_lab/posimai-api/server.js.backup-cors /volume1/docker/posimai_lab/posimai-api/server.js && head -29 /volume1/docker/posimai_lab/posimai-api/server.js > /tmp/server-final.js && echo '' if (!origin || allowedOrigins.includes(origin) || (origin && /^https:\\/\\/.*\\.vercel\\.app$/.test(origin))) cb(null, true);'' >> /tmp/server-final.js && tail -n +32 /volume1/docker/posimai_lab/posimai-api/server.js >> /tmp/server-final.js && mv /tmp/server-final.js /volume1/docker/posimai_lab/posimai-api/server.js && echo ''CORS configuration updated:'' && sed -n ''28,35p'' /volume1/docker/posimai_lab/posimai-api/server.js\")",
"Bash(ssh mai@100.76.7.3 \"/usr/local/bin/docker restart posimai_api && sleep 3 && /usr/local/bin/docker logs --tail 20 posimai_api\")",
"Bash(ssh mai@100.76.7.3 \"cp /volume1/docker/posimai_lab/posimai-api/server.js.backup-cors /volume1/docker/posimai_lab/posimai-api/server.js && head -29 /volume1/docker/posimai_lab/posimai-api/server.js > /tmp/s1.js && cat >> /tmp/s1.js << ''EOF''\n if (!origin || allowedOrigins.includes(origin) || (origin && /^https:\\/\\/.*\\.vercel\\.app$/.test(origin))) cb(null, true);\nEOF\ntail -n +32 /volume1/docker/posimai_lab/posimai-api/server.js >> /tmp/s1.js && mv /tmp/s1.js /volume1/docker/posimai_lab/posimai-api/server.js && sed -n ''28,35p'' /volume1/docker/posimai_lab/posimai-api/server.js\")",
"Bash(ssh mai@100.76.7.3 \"/usr/local/bin/docker restart posimai_api && sleep 3 && /usr/local/bin/docker logs --tail 15 posimai_api 2>&1 | grep -E ''🧠|Port|Error|listening''\")",
"Bash(ssh mai@100.76.7.3 \"/usr/local/bin/docker logs --tail 30 posimai_api 2>&1\")",
"Bash(ssh mai@100.76.7.3 \"sed -n ''28,35p'' /volume1/docker/posimai_lab/posimai-api/server.js.backup-cors\")",
"Bash(ssh mai@100.76.7.3 \"cp /volume1/docker/posimai_lab/posimai-api/server.js.backup-cors /volume1/docker/posimai_lab/posimai-api/server.js && cat > /tmp/cors-update.txt << ''EOF''\napp.use(cors({\n origin: (origin, cb) => {\n if (!origin || allowedOrigins.includes(origin) || (origin && /^https:\\/\\/.*\\.vercel\\.app$/.test(origin))) cb(null, true);\n else cb(new Error(''CORS not allowed''));\n },\n methods: [''GET'', ''POST'', ''PATCH'', ''DELETE'', ''OPTIONS''],\n allowedHeaders: [''Content-Type'', ''Authorization'']\n}));\nEOF\nhead -27 /volume1/docker/posimai_lab/posimai-api/server.js > /tmp/server-fixed.js && cat /tmp/cors-update.txt >> /tmp/server-fixed.js && tail -n +36 /volume1/docker/posimai_lab/posimai-api/server.js >> /tmp/server-fixed.js && mv /tmp/server-fixed.js /volume1/docker/posimai_lab/posimai-api/server.js && echo ''CORS updated:'' && sed -n ''28,36p'' /volume1/docker/posimai_lab/posimai-api/server.js\")",
"Bash(ssh mai@100.76.7.3 \"head -35 /volume1/docker/posimai_lab/posimai-api/server.js > /tmp/server-clean.js && tail -n +37 /volume1/docker/posimai_lab/posimai-api/server.js >> /tmp/server-clean.js && mv /tmp/server-clean.js /volume1/docker/posimai_lab/posimai-api/server.js && echo ''Fixed CORS section:'' && sed -n ''28,40p'' /volume1/docker/posimai_lab/posimai-api/server.js\")",
"Bash(ssh mai@100.76.7.3 \"/usr/local/bin/docker restart posimai_api && sleep 4 && /usr/local/bin/docker logs --tail 10 posimai_api 2>&1\")",
"Bash(curl -s -H \"Origin: https://posimai-feed.vercel.app\" -H \"Access-Control-Request-Method: GET\" -X OPTIONS \"https://posimai-lab.tail72e846.ts.net/brain/api/feed/media?user_id=default_user\" -i)",
"Bash(curl -s -H \"Origin: https://posimai-feed-kg2ej40wu-posimais-projects.vercel.app\" -H \"Access-Control-Request-Method: GET\" -X OPTIONS \"https://posimai-lab.tail72e846.ts.net/brain/api/feed/media?user_id=default_user\" -i)",
"Bash(ssh mai@100.76.7.3 \"grep -n ''POST.*feed/media'' /volume1/docker/posimai_lab/posimai-api/server.js\")",
"Bash(ssh mai@100.76.7.3 \"sed -n ''629,700p'' /volume1/docker/posimai_lab/posimai-api/server.js\")",
"Bash(ssh mai@100.76.7.3 \"/usr/local/bin/docker exec -i gitea_db psql -U gitea -d posimai_brain << ''EOF''\n\\d feed_media\nEOF\")",
"Bash(ssh mai@100.76.7.3 \"\n# Backup\ncp /volume1/docker/posimai_lab/posimai-api/server.js /volume1/docker/posimai_lab/posimai-api/server.js.backup-before-upsert\n\n# Replace POST /feed/media endpoint (lines 629-657)\nhead -628 /volume1/docker/posimai_lab/posimai-api/server.js > /tmp/server-upsert.js\ncat /tmp/feed-media-upsert.js >> /tmp/server-upsert.js\ntail -n +658 /volume1/docker/posimai_lab/posimai-api/server.js >> /tmp/server-upsert.js\n\n# Apply changes\nmv /tmp/server-upsert.js /volume1/docker/posimai_lab/posimai-api/server.js\n\n# Verify\necho ''Updated POST /feed/media endpoint:''\nsed -n ''629,665p'' /volume1/docker/posimai_lab/posimai-api/server.js\n\")",
"Bash(ssh mai@100.76.7.3 \"\n# Remove duplicate closing bracket at line 665\nhead -664 /volume1/docker/posimai_lab/posimai-api/server.js > /tmp/server-clean.js\ntail -n +666 /volume1/docker/posimai_lab/posimai-api/server.js >> /tmp/server-clean.js\nmv /tmp/server-clean.js /volume1/docker/posimai_lab/posimai-api/server.js\n\necho ''Cleaned up endpoint:''\nsed -n ''629,670p'' /volume1/docker/posimai_lab/posimai-api/server.js\n\")",
"Bash(ssh mai@100.76.7.3 \"/usr/local/bin/docker restart posimai_api && sleep 4 && /usr/local/bin/docker logs --tail 15 posimai_api 2>&1 | grep -E ''🧠|Port|Error|listening''\")",
"Bash(git commit -m \"$(cat <<''EOF''\nRefactor: Idempotent UPSERT for feed media management\n\n変更内容:\n本来あるべき姿へとリファクタリングしました。\n\n1. バックエンドAPI (server.js):\n - POST /feed/media エンドポイントをUPSERTパターンに変更\n - PostgreSQL ON CONFLICT構文を使用した冪等性の確保\n - 既存メディアの場合は更新して200 OK、新規の場合は201 Created\n - 409 Conflictエラーの撤廃\n\n2. フロントエンド (index.html):\n - オンボーディング時のメディア登録処理をクリーンアップ\n - デフォルトメディア非表示化処理から409エラーハンドリングを削除\n - シンプルに成功レスポンスのみを処理\n\n技術的改善:\n- HTTPセマンティクスの正しい実装: POST操作の冪等性\n- ユーザー体験の向上: コンソールエラーの撤廃\n- コードの可読性向上: エラーハンドリングのシンプル化\n- データ整合性: UNIQUE制約とON CONFLICTによる保証\n\nBEFORE:\n- 重複登録時に409 Conflictエラー → コンソールに赤いエラー表示\n- フロントエンドで409を特別扱い → 複雑なエラーハンドリング\n\nAFTER:\n- 重複登録時にUPSERTで更新 → 200 OKで正常完了\n- フロントエンドはシンプルに成功のみ処理 → クリーンなコード\n- コンソールエラーなし → プロフェッショナルなUX\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude <noreply@anthropic.com>\nEOF\n)\")",
"Bash(for app in posimai-analytics posimai-dashboard)",
"Read(//c/Users/maita/posimai-project/posimai-dashboard/$BASE/**)",
"Bash(vercel ls)"
],
"deny": [],
"ask": []
}
}

3
.gitignore vendored
View File

@ -21,6 +21,9 @@ out/
dist/
build/
# Tauri ビルド成果物(コミット禁止)
posimai-guard-app/src-tauri/target/
# VS Code 拡張バイナリローカル成果物、git管理外
*.vsix

View File

@ -0,0 +1 @@
[ 981ms] [ERROR] Failed to load resource: the server responded with a status of 401 () @ chrome-error://chromewebdata/:0

View File

@ -0,0 +1 @@
[ 756ms] [ERROR] Failed to load resource: the server responded with a status of 401 () @ chrome-error://chromewebdata/:0

View File

@ -0,0 +1 @@
[ 598ms] [ERROR] Failed to load resource: the server responded with a status of 404 () @ https://example.com/favicon.ico:0

View File

@ -0,0 +1,42 @@
[ 325ms] [ERROR] Failed to load resource: the server responded with a status of 401 () @ https://posimai-lens-git-main-posimais-projects.vercel.app/:0
[ 2101ms] [LOG] %c
__ __ _ __
\ \/ /___ __ __ _________ _____ (_)_ _______/ /_
\ / __ \/ / / / / ___/ __ `/ __ \ / / / / / ___/ __/
/ / /_/ / /_/ / / /__/ /_/ / / / / / / /_/ (__ ) /_
/_/\____/\__,_/ \___/\__,_/_/ /_/ __/ /\__,_/____/\__/
__ _ __ __ /___/
_____/ /_ (_)___ / /_/ /_ (_)___ ____ ______
/ ___/ __ \/ / __ \ / __/ __ \/ / __ \/ __ `/ ___/
(__ ) / / / / /_/ / / /_/ / / / / / / / /_/ (__ )
/____/_/ /_/_/ .___/ \__/_/ /_/_/_/ /_/\__, /____/
/_/ /____/
Hello!
- Open Source https://vercel.com/oss
- Careers https://vercel.com/careers
- Status https://vercel-status.com
font-family: "Geist Mono", monospace; white-space: pre; line-height: 1; @ https://vercel.com/_next/static/chunks/3vt9z9-00w7jo.js?dpl=dpl_3EQgGGg8UAHGMM9PQrASoz4KncR9:72
[ 2666ms] [WARNING] [GSI_LOGGER]: Your client application uses one of the Google One Tap prompt UI status methods that may stop functioning when FedCM becomes mandatory. Refer to the migration guide to update your code accordingly and opt-in to FedCM to test your changes. Learn more: https://developers.google.com/identity/gsi/web/guides/fedcm-migration?s=dc#display_moment and https://developers.google.com/identity/gsi/web/guides/fedcm-migration?s=dc#skipped_moment @ https://accounts.google.com/gsi/client:84
[ 2682ms] [ERROR] Failed to load resource: the server responded with a status of 403 () @ https://vercel.com/api/jwt:0
[ 2685ms] [ERROR] Not signed in with the identity provider. @ https://vercel.com/login?next=%2Fsso-api%3Furl%3Dhttps%253A%252F%252Fposimai-lens-git-main-posimais-projects.vercel.app%252F%26nonce%3D107866772336819a503666cc5b058bef89b52aa10c0c2165077a11945f474e73:0
[ 2844ms] [WARNING] Deprecated API for given entry type. @ https://vercel.com/_next/static/chunks/0835ycci1b4sv.js?dpl=dpl_3EQgGGg8UAHGMM9PQrASoz4KncR9:18
[ 6063ms] [WARNING] The resource https://vercel.com/_next/static/media/ebay-light.3ft_ki8203ov0.svg?dpl=dpl_3EQgGGg8UAHGMM9PQrASoz4KncR9 was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it has an appropriate `as` value and it is preloaded intentionally. @ https://vercel.com/login?next=%2Fsso-api%3Furl%3Dhttps%253A%252F%252Fposimai-lens-git-main-posimais-projects.vercel.app%252F%26nonce%3D107866772336819a503666cc5b058bef89b52aa10c0c2165077a11945f474e73:0
[ 6063ms] [WARNING] The resource https://vercel.com/_next/static/media/adobe-light.406_7oryl71va.svg?dpl=dpl_3EQgGGg8UAHGMM9PQrASoz4KncR9 was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it has an appropriate `as` value and it is preloaded intentionally. @ https://vercel.com/login?next=%2Fsso-api%3Furl%3Dhttps%253A%252F%252Fposimai-lens-git-main-posimais-projects.vercel.app%252F%26nonce%3D107866772336819a503666cc5b058bef89b52aa10c0c2165077a11945f474e73:0
[ 6064ms] [WARNING] The resource https://vercel.com/_next/static/chunks/1mkn1n4cf7-_i.css?dpl=dpl_3EQgGGg8UAHGMM9PQrASoz4KncR9 was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it has an appropriate `as` value and it is preloaded intentionally. @ https://vercel.com/login?next=%2Fsso-api%3Furl%3Dhttps%253A%252F%252Fposimai-lens-git-main-posimais-projects.vercel.app%252F%26nonce%3D107866772336819a503666cc5b058bef89b52aa10c0c2165077a11945f474e73:0
[ 6064ms] [WARNING] The resource https://vercel.com/_next/static/media/stripe-light.2l_1jctpe6k_4.svg?dpl=dpl_3EQgGGg8UAHGMM9PQrASoz4KncR9 was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it has an appropriate `as` value and it is preloaded intentionally. @ https://vercel.com/login?next=%2Fsso-api%3Furl%3Dhttps%253A%252F%252Fposimai-lens-git-main-posimais-projects.vercel.app%252F%26nonce%3D107866772336819a503666cc5b058bef89b52aa10c0c2165077a11945f474e73:0
[ 6064ms] [WARNING] The resource https://vercel.com/_next/static/media/stripe-dark.16ozjth2vgb_3.svg?dpl=dpl_3EQgGGg8UAHGMM9PQrASoz4KncR9 was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it has an appropriate `as` value and it is preloaded intentionally. @ https://vercel.com/login?next=%2Fsso-api%3Furl%3Dhttps%253A%252F%252Fposimai-lens-git-main-posimais-projects.vercel.app%252F%26nonce%3D107866772336819a503666cc5b058bef89b52aa10c0c2165077a11945f474e73:0
[ 6064ms] [WARNING] The resource https://vercel.com/_next/static/media/ebay-dark.34mho2a18qqlt.svg?dpl=dpl_3EQgGGg8UAHGMM9PQrASoz4KncR9 was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it has an appropriate `as` value and it is preloaded intentionally. @ https://vercel.com/login?next=%2Fsso-api%3Furl%3Dhttps%253A%252F%252Fposimai-lens-git-main-posimais-projects.vercel.app%252F%26nonce%3D107866772336819a503666cc5b058bef89b52aa10c0c2165077a11945f474e73:0
[ 31418ms] [ERROR] [GSI_LOGGER]: FedCM get() rejects with NetworkError: Error retrieving a token. @ https://vercel.com/_next/static/chunks/10rz-1kzad15u.js?dpl=dpl_3EQgGGg8UAHGMM9PQrASoz4KncR9:2
[ 31834ms] [ERROR] Failed to load resource: the server responded with a status of 429 () @ https://o205439.ingest.sentry.io/api/1323670/envelope/?sentry_key=d00780d432ac4ccf882f60dd02062e14&sentry_version=7&sentry_client=sentry.javascript.browser%2F7.111.0:0
[ 34914ms] [WARNING] The resource https://vercel.com/_next/static/media/ebay-light.3ft_ki8203ov0.svg?dpl=dpl_3EQgGGg8UAHGMM9PQrASoz4KncR9 was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it has an appropriate `as` value and it is preloaded intentionally. @ https://vercel.com/login?next=%2Fsso-api%3Furl%3Dhttps%253A%252F%252Fposimai-lens-git-main-posimais-projects.vercel.app%252F%26nonce%3D107866772336819a503666cc5b058bef89b52aa10c0c2165077a11945f474e73:0
[ 34914ms] [WARNING] The resource https://vercel.com/_next/static/media/adobe-light.406_7oryl71va.svg?dpl=dpl_3EQgGGg8UAHGMM9PQrASoz4KncR9 was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it has an appropriate `as` value and it is preloaded intentionally. @ https://vercel.com/login?next=%2Fsso-api%3Furl%3Dhttps%253A%252F%252Fposimai-lens-git-main-posimais-projects.vercel.app%252F%26nonce%3D107866772336819a503666cc5b058bef89b52aa10c0c2165077a11945f474e73:0
[ 34914ms] [WARNING] The resource https://vercel.com/_next/static/chunks/1mkn1n4cf7-_i.css?dpl=dpl_3EQgGGg8UAHGMM9PQrASoz4KncR9 was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it has an appropriate `as` value and it is preloaded intentionally. @ https://vercel.com/login?next=%2Fsso-api%3Furl%3Dhttps%253A%252F%252Fposimai-lens-git-main-posimais-projects.vercel.app%252F%26nonce%3D107866772336819a503666cc5b058bef89b52aa10c0c2165077a11945f474e73:0
[ 34914ms] [WARNING] The resource https://vercel.com/_next/static/media/stripe-light.2l_1jctpe6k_4.svg?dpl=dpl_3EQgGGg8UAHGMM9PQrASoz4KncR9 was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it has an appropriate `as` value and it is preloaded intentionally. @ https://vercel.com/login?next=%2Fsso-api%3Furl%3Dhttps%253A%252F%252Fposimai-lens-git-main-posimais-projects.vercel.app%252F%26nonce%3D107866772336819a503666cc5b058bef89b52aa10c0c2165077a11945f474e73:0
[ 34914ms] [WARNING] The resource https://vercel.com/_next/static/media/stripe-dark.16ozjth2vgb_3.svg?dpl=dpl_3EQgGGg8UAHGMM9PQrASoz4KncR9 was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it has an appropriate `as` value and it is preloaded intentionally. @ https://vercel.com/login?next=%2Fsso-api%3Furl%3Dhttps%253A%252F%252Fposimai-lens-git-main-posimais-projects.vercel.app%252F%26nonce%3D107866772336819a503666cc5b058bef89b52aa10c0c2165077a11945f474e73:0
[ 34914ms] [WARNING] The resource https://vercel.com/_next/static/media/ebay-dark.34mho2a18qqlt.svg?dpl=dpl_3EQgGGg8UAHGMM9PQrASoz4KncR9 was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it has an appropriate `as` value and it is preloaded intentionally. @ https://vercel.com/login?next=%2Fsso-api%3Furl%3Dhttps%253A%252F%252Fposimai-lens-git-main-posimais-projects.vercel.app%252F%26nonce%3D107866772336819a503666cc5b058bef89b52aa10c0c2165077a11945f474e73:0

View File

@ -0,0 +1,35 @@
[ 533ms] [LOG] %c
________ ___ ____ ________ __
/_ __/ /_ ___ / | / _/ / ____/ /___ __ ______/ /
/ / / __ \/ _ \ / /| | / / / / / / __ \/ / / / __ /
/ / / / / / __/ / ___ |_/ / / /___/ / /_/ / /_/ / /_/ /
/_/ /_/ /_/\___/ /_/ |_/___/ \____/_/\____/\__,_/\__,_/
Hello!
- Open Source https://vercel.com/oss
- Careers https://vercel.com/careers
- Status https://vercel-status.com
font-family: "Geist Mono", monospace; white-space: pre; line-height: 1; @ https://vercel.com/_next/static/chunks/3vt9z9-00w7jo.js?dpl=dpl_3EQgGGg8UAHGMM9PQrASoz4KncR9:72
[ 1361ms] [WARNING] [GSI_LOGGER]: Your client application uses one of the Google One Tap prompt UI status methods that may stop functioning when FedCM becomes mandatory. Refer to the migration guide to update your code accordingly and opt-in to FedCM to test your changes. Learn more: https://developers.google.com/identity/gsi/web/guides/fedcm-migration?s=dc#display_moment and https://developers.google.com/identity/gsi/web/guides/fedcm-migration?s=dc#skipped_moment @ https://accounts.google.com/gsi/client:84
[ 1528ms] [ERROR] Failed to load resource: the server responded with a status of 403 () @ https://vercel.com/api/jwt:0
[ 1530ms] [ERROR] Not signed in with the identity provider. @ https://vercel.com/login?next=%2Fposimais-projects%2Fposimai-lens%2Fsettings%2Fdeployment-protection:0
[ 1864ms] [WARNING] Deprecated API for given entry type. @ https://vercel.com/_next/static/chunks/0835ycci1b4sv.js?dpl=dpl_3EQgGGg8UAHGMM9PQrASoz4KncR9:18
[ 5232ms] [ERROR] [GSI_LOGGER]: FedCM get() rejects with NetworkError: Error retrieving a token. @ https://vercel.com/_next/static/chunks/10rz-1kzad15u.js?dpl=dpl_3EQgGGg8UAHGMM9PQrASoz4KncR9:2
[ 5456ms] [WARNING] The resource https://vercel.com/_next/static/media/ebay-light.3ft_ki8203ov0.svg?dpl=dpl_3EQgGGg8UAHGMM9PQrASoz4KncR9 was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it has an appropriate `as` value and it is preloaded intentionally. @ https://vercel.com/login?next=%2Fposimais-projects%2Fposimai-lens%2Fsettings%2Fdeployment-protection:0
[ 5456ms] [WARNING] The resource https://vercel.com/_next/static/media/adobe-light.406_7oryl71va.svg?dpl=dpl_3EQgGGg8UAHGMM9PQrASoz4KncR9 was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it has an appropriate `as` value and it is preloaded intentionally. @ https://vercel.com/login?next=%2Fposimais-projects%2Fposimai-lens%2Fsettings%2Fdeployment-protection:0
[ 5456ms] [WARNING] The resource https://vercel.com/_next/static/chunks/1mkn1n4cf7-_i.css?dpl=dpl_3EQgGGg8UAHGMM9PQrASoz4KncR9 was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it has an appropriate `as` value and it is preloaded intentionally. @ https://vercel.com/login?next=%2Fposimais-projects%2Fposimai-lens%2Fsettings%2Fdeployment-protection:0
[ 5456ms] [WARNING] The resource https://vercel.com/_next/static/media/stripe-light.2l_1jctpe6k_4.svg?dpl=dpl_3EQgGGg8UAHGMM9PQrASoz4KncR9 was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it has an appropriate `as` value and it is preloaded intentionally. @ https://vercel.com/login?next=%2Fposimais-projects%2Fposimai-lens%2Fsettings%2Fdeployment-protection:0
[ 5456ms] [WARNING] The resource https://vercel.com/_next/static/media/stripe-dark.16ozjth2vgb_3.svg?dpl=dpl_3EQgGGg8UAHGMM9PQrASoz4KncR9 was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it has an appropriate `as` value and it is preloaded intentionally. @ https://vercel.com/login?next=%2Fposimais-projects%2Fposimai-lens%2Fsettings%2Fdeployment-protection:0
[ 5456ms] [WARNING] The resource https://vercel.com/_next/static/media/ebay-dark.34mho2a18qqlt.svg?dpl=dpl_3EQgGGg8UAHGMM9PQrASoz4KncR9 was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it has an appropriate `as` value and it is preloaded intentionally. @ https://vercel.com/login?next=%2Fposimais-projects%2Fposimai-lens%2Fsettings%2Fdeployment-protection:0
[ 5478ms] [ERROR] Failed to load resource: the server responded with a status of 429 () @ https://o205439.ingest.sentry.io/api/1323670/envelope/?sentry_key=d00780d432ac4ccf882f60dd02062e14&sentry_version=7&sentry_client=sentry.javascript.browser%2F7.111.0:0
[ 8549ms] [WARNING] The resource https://vercel.com/_next/static/media/ebay-light.3ft_ki8203ov0.svg?dpl=dpl_3EQgGGg8UAHGMM9PQrASoz4KncR9 was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it has an appropriate `as` value and it is preloaded intentionally. @ https://vercel.com/login?next=%2Fposimais-projects%2Fposimai-lens%2Fsettings%2Fdeployment-protection:0
[ 8549ms] [WARNING] The resource https://vercel.com/_next/static/media/adobe-light.406_7oryl71va.svg?dpl=dpl_3EQgGGg8UAHGMM9PQrASoz4KncR9 was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it has an appropriate `as` value and it is preloaded intentionally. @ https://vercel.com/login?next=%2Fposimais-projects%2Fposimai-lens%2Fsettings%2Fdeployment-protection:0
[ 8549ms] [WARNING] The resource https://vercel.com/_next/static/chunks/1mkn1n4cf7-_i.css?dpl=dpl_3EQgGGg8UAHGMM9PQrASoz4KncR9 was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it has an appropriate `as` value and it is preloaded intentionally. @ https://vercel.com/login?next=%2Fposimais-projects%2Fposimai-lens%2Fsettings%2Fdeployment-protection:0
[ 8549ms] [WARNING] The resource https://vercel.com/_next/static/media/stripe-light.2l_1jctpe6k_4.svg?dpl=dpl_3EQgGGg8UAHGMM9PQrASoz4KncR9 was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it has an appropriate `as` value and it is preloaded intentionally. @ https://vercel.com/login?next=%2Fposimais-projects%2Fposimai-lens%2Fsettings%2Fdeployment-protection:0
[ 8549ms] [WARNING] The resource https://vercel.com/_next/static/media/stripe-dark.16ozjth2vgb_3.svg?dpl=dpl_3EQgGGg8UAHGMM9PQrASoz4KncR9 was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it has an appropriate `as` value and it is preloaded intentionally. @ https://vercel.com/login?next=%2Fposimais-projects%2Fposimai-lens%2Fsettings%2Fdeployment-protection:0
[ 8550ms] [WARNING] The resource https://vercel.com/_next/static/media/ebay-dark.34mho2a18qqlt.svg?dpl=dpl_3EQgGGg8UAHGMM9PQrASoz4KncR9 was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it has an appropriate `as` value and it is preloaded intentionally. @ https://vercel.com/login?next=%2Fposimais-projects%2Fposimai-lens%2Fsettings%2Fdeployment-protection:0

View File

@ -0,0 +1,6 @@
- generic [ref=e2]:
- heading "Example Domain" [level=1] [ref=e3]
- paragraph [ref=e4]: This domain is for use in documentation examples without needing permission. Avoid use in operations.
- paragraph [ref=e5]:
- link "Learn more" [ref=e6] [cursor=pointer]:
- /url: https://iana.org/domains/example

View File

@ -0,0 +1,49 @@
- generic [active] [ref=e1]:
- link "Skip to content":
- /url: "#geist-skip-nav"
- generic [ref=e3]:
- banner [ref=e4]:
- link "Vercel logo":
- /url: /home
- button "Vercel Logo":
- img "Vercel Logo"
- navigation [ref=e5]:
- navigation [ref=e6]:
- link "Sign Up" [ref=e7] [cursor=pointer]:
- /url: /signup?next=%2Fsso-api%3Furl%3Dhttps%253A%252F%252Fposimai-lens-git-main-posimais-projects.vercel.app%252F%26nonce%3D107866772336819a503666cc5b058bef89b52aa10c0c2165077a11945f474e73
- paragraph [ref=e9]: Sign Up
- main [ref=e10]:
- generic [ref=e12]:
- heading "Log in to Vercel" [level=1] [ref=e15]
- generic [ref=e16]:
- generic [ref=e17]:
- textbox "Email Address" [ref=e19]
- button "Continue with Email" [ref=e21] [cursor=pointer]:
- generic [ref=e22]: Continue with Email
- generic [ref=e24]:
- button "Continue with Google" [ref=e25] [cursor=pointer]:
- img [ref=e28]
- generic [ref=e34]: Continue with Google
- button "Continue with GitHub" [ref=e35] [cursor=pointer]:
- img [ref=e37]
- generic [ref=e41]: Continue with GitHub
- button "Continue with Apple" [ref=e42] [cursor=pointer]:
- img [ref=e44]
- generic [ref=e47]: Continue with Apple
- button "Continue with SAML SSO" [ref=e49] [cursor=pointer]:
- img [ref=e51]
- generic [ref=e53]: Continue with SAML SSO
- button "Continue with Passkey" [ref=e54] [cursor=pointer]:
- img [ref=e56]
- generic [ref=e58]: Continue with Passkey
- button "Show other options" [ref=e59] [cursor=pointer]:
- generic [ref=e60]: Show other options
- paragraph [ref=e62]:
- text: Don't have an account?
- link "Sign Up" [ref=e63] [cursor=pointer]:
- /url: /signup?next=%2Fsso-api%3Furl%3Dhttps%253A%252F%252Fposimai-lens-git-main-posimais-projects.vercel.app%252F%26nonce%3D107866772336819a503666cc5b058bef89b52aa10c0c2165077a11945f474e73
- generic [ref=e66]:
- link "Terms" [ref=e67] [cursor=pointer]:
- /url: /legal/terms
- link "Privacy Policy" [ref=e68] [cursor=pointer]:
- /url: /legal/privacy-policy

View File

@ -0,0 +1,55 @@
- generic [active] [ref=e1]:
- link "Skip to content":
- /url: "#geist-skip-nav"
- generic [ref=e3]:
- banner [ref=e4]:
- link "Vercel logo":
- /url: /home
- button "Vercel Logo":
- img "Vercel Logo"
- navigation [ref=e5]:
- navigation [ref=e6]:
- link "Sign Up" [ref=e7] [cursor=pointer]:
- /url: /signup?next=%2Fposimais-projects%2Fposimai-lens%2Fsettings%2Fdeployment-protection
- paragraph [ref=e9]: Sign Up
- main [ref=e10]:
- generic [ref=e12]:
- heading "Log in to Vercel" [level=1] [ref=e15]
- generic [ref=e16]:
- generic [ref=e17]:
- textbox "Email Address" [ref=e19]
- button "Continue with Email" [ref=e21] [cursor=pointer]:
- generic [ref=e22]: Continue with Email
- generic [ref=e24]:
- button "Continue with Google" [ref=e25] [cursor=pointer]:
- img [ref=e28]
- generic [ref=e34]: Continue with Google
- button "Continue with GitHub" [ref=e35] [cursor=pointer]:
- img [ref=e37]
- generic [ref=e41]: Continue with GitHub
- button "Continue with Apple" [ref=e42] [cursor=pointer]:
- img [ref=e44]
- generic [ref=e47]: Continue with Apple
- button "Continue with SAML SSO" [ref=e49] [cursor=pointer]:
- img [ref=e51]
- generic [ref=e53]: Continue with SAML SSO
- button "Continue with Passkey" [ref=e54] [cursor=pointer]:
- img [ref=e56]
- generic [ref=e58]: Continue with Passkey
- button "Show other options" [ref=e59] [cursor=pointer]:
- generic [ref=e60]: Show other options
- paragraph [ref=e62]:
- text: Don't have an account?
- link "Sign Up" [ref=e63] [cursor=pointer]:
- /url: /signup?next=%2Fposimais-projects%2Fposimai-lens%2Fsettings%2Fdeployment-protection
- generic [ref=e66]:
- link "Terms" [ref=e67] [cursor=pointer]:
- /url: /legal/terms
- link "Privacy Policy" [ref=e68] [cursor=pointer]:
- /url: /legal/privacy-policy
- alert [ref=e69]
- generic:
- generic:
- generic:
- generic:
- img

1
.serena/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/cache

149
.serena/project.yml Normal file
View File

@ -0,0 +1,149 @@
# the name by which the project can be referenced within Serena
project_name: "posimai-project"
# list of languages for which language servers are started; choose from:
# al bash clojure cpp csharp
# csharp_omnisharp dart elixir elm erlang
# fortran fsharp go groovy haskell
# java julia kotlin lua markdown
# matlab nix pascal perl php
# php_phpactor powershell python python_jedi r
# rego ruby ruby_solargraph rust scala
# swift terraform toml typescript typescript_vts
# vue yaml zig
# (This list may be outdated. For the current list, see values of Language enum here:
# https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py
# For some languages, there are alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.)
# Note:
# - For C, use cpp
# - For JavaScript, use typescript
# - For Free Pascal/Lazarus, use pascal
# Special requirements:
# Some languages require additional setup/installations.
# See here for details: https://oraios.github.io/serena/01-about/020_programming-languages.html#language-servers
# When using multiple languages, the first language server that supports a given file will be used for that file.
# The first language is the default language and the respective language server will be used as a fallback.
# Note that when using the JetBrains backend, language servers are not used and this list is correspondingly ignored.
languages:
- typescript
# the encoding used by text files in the project
# For a list of possible encodings, see https://docs.python.org/3.11/library/codecs.html#standard-encodings
encoding: "utf-8"
# The language backend to use for this project.
# If not set, the global setting from serena_config.yml is used.
# Valid values: LSP, JetBrains
# Note: the backend is fixed at startup. If a project with a different backend
# is activated post-init, an error will be returned.
language_backend:
# whether to use project's .gitignore files to ignore files
ignore_all_files_in_gitignore: true
# list of additional paths to ignore in this project.
# Same syntax as gitignore, so you can use * and **.
# Note: global ignored_paths from serena_config.yml are also applied additively.
ignored_paths: []
# whether the project is in read-only mode
# If set to true, all editing tools will be disabled and attempts to use them will result in an error
# Added on 2025-04-18
read_only: false
# list of tool names to exclude. We recommend not excluding any tools, see the readme for more details.
# Below is the complete list of tools for convenience.
# To make sure you have the latest list of tools, and to view their descriptions,
# execute `uv run scripts/print_tool_overview.py`.
#
# * `activate_project`: Activates a project by name.
# * `check_onboarding_performed`: Checks whether project onboarding was already performed.
# * `create_text_file`: Creates/overwrites a file in the project directory.
# * `delete_lines`: Deletes a range of lines within a file.
# * `delete_memory`: Deletes a memory from Serena's project-specific memory store.
# * `execute_shell_command`: Executes a shell command.
# * `find_referencing_code_snippets`: Finds code snippets in which the symbol at the given location is referenced.
# * `find_referencing_symbols`: Finds symbols that reference the symbol at the given location (optionally filtered by type).
# * `find_symbol`: Performs a global (or local) search for symbols with/containing a given name/substring (optionally filtered by type).
# * `get_current_config`: Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes.
# * `get_symbols_overview`: Gets an overview of the top-level symbols defined in a given file.
# * `initial_instructions`: Gets the initial instructions for the current project.
# Should only be used in settings where the system prompt cannot be set,
# e.g. in clients you have no control over, like Claude Desktop.
# * `insert_after_symbol`: Inserts content after the end of the definition of a given symbol.
# * `insert_at_line`: Inserts content at a given line in a file.
# * `insert_before_symbol`: Inserts content before the beginning of the definition of a given symbol.
# * `list_dir`: Lists files and directories in the given directory (optionally with recursion).
# * `list_memories`: Lists memories in Serena's project-specific memory store.
# * `onboarding`: Performs onboarding (identifying the project structure and essential tasks, e.g. for testing or building).
# * `prepare_for_new_conversation`: Provides instructions for preparing for a new conversation (in order to continue with the necessary context).
# * `read_file`: Reads a file within the project directory.
# * `read_memory`: Reads the memory with the given name from Serena's project-specific memory store.
# * `remove_project`: Removes a project from the Serena configuration.
# * `replace_lines`: Replaces a range of lines within a file with new content.
# * `replace_symbol_body`: Replaces the full definition of a symbol.
# * `restart_language_server`: Restarts the language server, may be necessary when edits not through Serena happen.
# * `search_for_pattern`: Performs a search for a pattern in the project.
# * `summarize_changes`: Provides instructions for summarizing the changes made to the codebase.
# * `switch_modes`: Activates modes by providing a list of their names
# * `think_about_collected_information`: Thinking tool for pondering the completeness of collected information.
# * `think_about_task_adherence`: Thinking tool for determining whether the agent is still on track with the current task.
# * `think_about_whether_you_are_done`: Thinking tool for determining whether the task is truly completed.
# * `write_memory`: Writes a named memory (for future reference) to Serena's project-specific memory store.
excluded_tools: []
# list of tools to include that would otherwise be disabled (particularly optional tools that are disabled by default)
included_optional_tools: []
# fixed set of tools to use as the base tool set (if non-empty), replacing Serena's default set of tools.
# This cannot be combined with non-empty excluded_tools or included_optional_tools.
fixed_tools: []
# list of mode names to that are always to be included in the set of active modes
# The full set of modes to be activated is base_modes + default_modes.
# If the setting is undefined, the base_modes from the global configuration (serena_config.yml) apply.
# Otherwise, this setting overrides the global configuration.
# Set this to [] to disable base modes for this project.
# Set this to a list of mode names to always include the respective modes for this project.
base_modes:
# list of mode names that are to be activated by default.
# The full set of modes to be activated is base_modes + default_modes.
# If the setting is undefined, the default_modes from the global configuration (serena_config.yml) apply.
# Otherwise, this overrides the setting from the global configuration (serena_config.yml).
# This setting can, in turn, be overridden by CLI parameters (--mode).
default_modes:
# initial prompt for the project. It will always be given to the LLM upon activating the project
# (contrary to the memories, which are loaded on demand).
initial_prompt: ""
# time budget (seconds) per tool call for the retrieval of additional symbol information
# such as docstrings or parameter information.
# This overrides the corresponding setting in the global configuration; see the documentation there.
# If null or missing, use the setting from the global configuration.
symbol_info_budget:
# list of regex patterns which, when matched, mark a memory entry as readonly.
# Extends the list from the global configuration, merging the two lists.
read_only_memory_patterns: []
# line ending convention to use when writing source files.
# Possible values: unset (use global setting), "lf", "crlf", or "native" (platform default)
# This does not affect Serena's own files (e.g. memories and configuration files), which always use native line endings.
line_ending:
# list of regex patterns for memories to completely ignore.
# Matching memories will not appear in list_memories or activate_project output
# and cannot be accessed via read_memory or write_memory.
# To access ignored memory files, use the read_file tool on the raw file path.
# Extends the list from the global configuration, merging the two lists.
# Example: ["_archive/.*", "_episodes/.*"]
ignored_memory_patterns: []
# advanced configuration option allowing to configure language server-specific options.
# Maps the language key to the options.
# Have a look at the docstring of the constructors of the LS implementations within solidlsp (e.g., for C# or PHP) to see which options are available.
# No documentation on options means no options are available.
ls_specific_settings: {}

View File

@ -1,20 +0,0 @@
// Syncthing ignore file for posimai-project
// IMPORTANT: .git を同期すると git インデックスが破損するため必須
// Git 内部ファイル(絶対に同期しない)
.git
// 依存関係・ビルド成果物(大量ファイル、同期不要)
node_modules
.next
out
dist
build
// 環境変数(秘密情報、意図的にリポジトリ外)
.env
.env.local
// OS 一時ファイル
.DS_Store
Thumbs.db

16
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,16 @@
{
"guard.excludeDirs": [
"node_modules",
".next",
".git",
"dist",
"build",
"out",
".turbo",
".cache",
"coverage",
"__pycache__",
".venv",
"venv"
]
}

View File

@ -77,7 +77,7 @@ Station・Guard・その他サイバー系アプリで使うダークブルー
[data-app-id="posimai-atlas"] { --accent: #22D3EE; } /* Cyan */
[data-app-id="posimai-dev"] { --accent: #A78BFA; } /* Violet */
[data-app-id="posimai-journal"]{ --accent: #80CAEE; } /* Sky-Blue */
[data-app-id="posimai-guard"] { --accent: #F97316; } /* Amber-Orange */
[data-app-id="posimai-guard"] { --accent: #22D3EE; } /* Cyan — hacker/security */
/* NG: ルートへの直書き上書き */
:root { --accent: #22D3EE; }
@ -89,7 +89,7 @@ Station・Guard・その他サイバー系アプリで使うダークブルー
| posimai-journal / posimai-site | `#80CAEE` Sky-Blue | 静かで知的な印象 |
| posimai-atlas | `#22D3EE` Cyan | サイバー・ターミナル感。背景も `#0C1221` navy |
| posimai-dev | `#A78BFA` Violet | コード・AI・ターミナルの融合 |
| posimai-guard | `#F97316` Amber-Orange | セキュリティ・警告の視覚的コンテキスト |
| posimai-guard | `#22D3EE` Cyan | ハッカーツール・サイバーセキュリティのコンテキスト |
---

View File

@ -9,10 +9,16 @@
## 次にやること(優先順)
### Guard / Tauri
1. **ルールエンジンを共有パッケージに切り出し**`packages/rule-engine/` として独立させ、アプリ・拡張・CLI が全部参照する構成へ
2. **VS Code 拡張posimai-guard-extにルールエンジン組み込み** — 現在 Gemini 依存。共有パッケージ化後に差し替え
3. **CLI 作成**`npx @posimai/guard scan .` 形式。共有パッケージ化後に実装
4. **Tauri アプリ動作確認** — デスクトップショートカットから起動、CodeViewer が WebView でも動くか確認
1. **Timer → Pulse 連携(セッションログ)** — タイマー終了時にセッション記録モーダル。`posimai-timer-sessions` に保存
2. **Tauri アプリ動作確認** — デスクトップショートカットから起動、CodeViewer が WebView でも動くか確認
3. **Diff → 履歴保存 + Journal 送信** — 比較結果をlocalStorage保存・Journal連携
### Guard 完成状態2026-04-14
- ルールエンジン133ルール
- 静的修正テンプレート85ルール
- VS Code で開く ✓
- CLI`npx tsx cli/guard.ts`)✓
- VS Code 拡張APIキー不要
### Guard ビルド方法メモ(次回再ビルド時)
```bash
@ -50,6 +56,14 @@ node_modules/.bin/tauri build
| user_activity テーブル追加 | mai 確認必要DB スキーマ変更) | Digest 週次集計の前提。CLAUDE.md 要確認事項 |
| Redis 移行webauthnChallenges | スケール要件が出てから | 現状インメモリで問題なし |
## 直近でやったこと2026-04-14 セッション2
### ponshu / Guard 拡張 / Guard VS Code修正
- **ponshu_room_lite 消費者APK**: 最新コミット04-12 13:23でリビルド完了 → `build/apk_releases/2026-04-14_22-54-28/` に maita・Eiji 向け各 90MB
- **posimai-guard-ext**: ルールエンジン組み込み完了。APIキー不要でスキャン可能に。Gemini/Claude はオプション追加スキャンに降格。バンドル 120KBルールエンジン込み
- **Guard「VS Code で開く」修正**: `window.open(uri, '_self')``window.location.href = uri` に変更。popup ブロッカーによる無効化を解消
- デプロイ済みVercel 自動デプロイ中)
## 直近でやったこと2026-04-14
### Guard 静的修正テンプレート + セキュリティ修正

View File

@ -0,0 +1,68 @@
# Posimai Auto-Blogging System (Architect without Code) 開発提案プロンプト
## Claude Code へのミッション
現在、非エンジニアでありながらAIを駆使してアーキテクチャを設計・開発している「Posimai」エコシステムの専属アーキテクトユーザーの活動を、完全自動でブログ化・コンテンツ化するプロジェクトを立ち上げようとしています。
このドキュメントは、そのシステムの設計をあなたClaude Codeと共に進めるための前提情報と要求事項をまとめたものです。
あなたのミッションは以下の3点です。
1. **批判的レビュー**: 下記に記載された「Geminiの提案」「Antigravityの提案」の2つについて、技術的実現性・運用保守性・「Neo-Minimalism 2.0」への適合性の観点から容赦なくレビューしてください。
2. **ゼロベースでの再設計**: それらを踏まえ、よりシンプルで堅牢、かつユーザーの入力負担が極限までゼロに近づく「第3のベストプラクティス」があれば提案してください。
3. **安全な実装**: 方針決定後、既存アプリ群posimai-hotels等に一切の悪影響を与えない完全分離された形でプロトタイプを構築してください。
---
## 1. プロジェクトの要件とコンテキスト
- **ターゲット層**: DXに挫折した中小企業担当者、コードは書けないがAI開発に興味がある層。
- **コンセプト(ペルソナ像の極意)**: **「Architect without Code (コードは書けないが、アーキテクチャは支配する)」**
- 単なる「ITが苦手な非エンジニア」のステレオタイプではない。**実はCUI黒い画面やハッカー的な美学、サイバー感バイナリー等が大好き**という独自のギャップを持つ。コードは自筆しないが、ターミナルで流れるログを見たりAIと高度な議論を戦わせることに熱狂するタイプ。
- **インプットのバランス(自動抽出と手動のハイブリッド)**: 基本的な「すったもんだの軌跡」や「感情の揺れエラーに絶望する・サイバーなUIに喜ぶ等」は、**手動入力に依存せず、無意識のログ普段AIと交わしているチャットの熱量やコマンド履歴**からAI自身が自動抽出し、ベースとなる案を勝手に作り上げることを理想とする。しかし、同時に**ユーザー自身が「一言添えたい」と思った時に手動で感情やポエムを注入できる柔軟な仕組み(`diary.md`等)も共存**させること。
- **実行環境制限**: 既存のアプリ群(`apps/` や既存ディレクトリ)には一切の干渉・改変を行わず、物理的に完全に隔離された専用ディレクトリ(例: `tools/posimai-scribe/`)を作成し、その内部のみで魔法(システム)を構築すること。
---
## 2. 過去のAIからの提案内容
### 【案AGeminiの提案インビジブル・ロギングAPI連携型
- **仕組み**:
- Synology NAS上にn8nを構築。
- Web上のGemini/Claudeとの対話履歴をスクレイピング/APIで自動抽出。
- GitHubバックアップやVercelデプロイWebhookと情報を結合。
- APIを使ってnoteへ自動投稿非公開API/自動ブラウザ操作等で回避)。
- **AntigravityGemini 3.1 Proからの懸念**:
- Web版チャット履歴の安定した自動取得はAPIが存在しないため非常に脆い。
- noteの連携はBANリスクとUI変更に弱く保守コストが甚大。全体的に「Over Engineering」になりがち。
- **Gemini自身からの追加クリティカル・レビュー**:
- **「認証の壁」**: ヘッドレスブラウザ等でのログイン維持はメンテナンス地獄を招き、Posimaiの「手間をかけない」哲学に反する。
- **「データの断片化」**: noteへの投稿は資産が第三者プラットフォームに紐付くため、「Your data is yours (データ主権)」という自由追求の思想と衝突する。
### 【案BAntigravityの提案ローカルGit・ファイル駆動型
- **仕組み**:
- Webチャットを追うのを諦め、「ローカルの記録」をソースの真実とする。
- `posimai-project` 内のGit差分Diff、AIとの作業記録`task.md`や`claude-memory`)、および追加の `diary.md`(ポロリと感情を書く用)をソースとする。
- 1日の終わりに特定のNodeスクリプト例: `scripts/blog-generator.js`を叩くと、AIClaudeのAPI等が上記を読み込んでMarkdown記事を自動生成する。
- 生成されたMarkdownをZennGitHub連携対応や新規のPWAposimai-journalにPushすることで完全自動デプロイ。
- **Antigravityからの懸念**:
- 安定・確実だが、「対話中の生々しいやり取りAIが嘘をついた等」はコード差分だけからは拾いにくく、`diary.md` への書き込み等に依存してしまう可能性あり。
- **Geminiからの追加クリティカル・レビュー**:
- **「文脈の欠落」**: GitのDiff差分は「何をしたか」は語るが、「なぜそうしたかAIとの議論の末の決断」というドラマが抜け落ちる。
- **「感情の強制」**: `diary.md` への記入すら、調子が悪い時には「タスク」に感じてしまい、結果的にロギングが破綻する完全自動化に反する懸念。
---
## 3. Claude Codeへの「第3の答え」への期待値ヒント
Claude Codeには、上記の制約と批判を踏まえ、以下の「技術的な飛躍」を組み込んだゼロベースの議論を期待します。
1. **MCPModel Context Protocolの活用**:
現在構築済みのSynology NAS上のMCPサーバーや、Cursor/Claude Code自体のコンテキスト保持機能を逆利用し、**「AIが自分自身の思考ログをエクスポートする」**仕組みを模索することは可能か?
2. **「Shadow Logging」**:
開発中にターミナルに流れるエラーログや、AIへのプロンプトを「バックグラウンドで一時ファイルに追記し続ける」常駐デーモン監視プログラムを作成し、`diary.md` を書く手間すら完全に省くアプローチは可能か?
---
## 4. Claude Codeへの最初のアクション要求
1. このファイルを読み込み、案Aと案Bについて独自の視点も交えて**批判的に分析**し、第3の道の可能性を議論してください。
2. これらを統合・昇華させた、今の `posimai-project` のエコシステムに最も適した**「アーキテクチャ案」**を提案してください。
3. ユーザーの承認後、「既存アプリは神域である」という絶対ルールの元、物理的に隔離された `tools/posimai-scribe/` ディレクトリ下での開発に着手してください。

View File

@ -0,0 +1,54 @@
# Posimai Project - Neo-Minimalism 2.0 と事業アーキテクチャ指針
最終更新: 2026-04-02
作成経緯: 開発者mai、Gemini、Antigravityとの間で完結した事業戦略・UX・インフラ設計の最終合意内容の記録。Claude CodeUbuntu PC側へのコンテキスト共有用。
---
## 1. 哲学Neo-Minimalism 2.0 の定義
「Neo-Minimalism 2.0」とは、単なる視覚的な簡素化ではなく、個人のデジタル帝国を築くための生存戦略と自由を最大化する独自のアーキテクチャ指針である。
### ① Visual Layerクリーンな情報整理
- **Teal (#6EE7B7) の一貫性**: 視認性が高く、清潔感と先進性を与えるアクセントカラーのみを使用。
- **余白の機能化**: 装飾的グラフィックを排除し、余白そのものを情報区切りのUIとして扱う。
- **絵文字の完全排除**: プロ意識と理知的な印象を保ち、情報のノイズを防ぐ。
### ② Architecture Layerセルフホストと自立
- **脱ベンダーロックイン**: Firebase等に依存せず、VPS・Synology NAS (Docker)・PostgreSQLを掌握。
- **PWAファースト**: App Storeの審査に縛られず、Web技術でネイティブ体験を最小構成で届ける。
- **最小限の認証**: Magic Link + PasskeyWebAuthnを採用し、ユーザーからパスワード管理の負担を消し去る。
### ③ Business Layer誠実なマネタイズ
- **広告・サブスクの排除**: ユーザーの注意力や精神的自由を奪うモデルを徹底排除。
- **データ主権 (Local-first)**: ユーザーのデータを人質に取らない。
- **BYOK (Bring Your Own Key)**: 非エンジニアには隠しつつも、設定奥底に自前APIキー持ち込みの裏口を用意し、究極の永続性を保証する。
---
## 2. アプリ統合戦略:デジタル帝国の分権
巨大な一つのスーパーアプリポータルを作ることはNeo-Minimalism 2.0に反する。Unix哲学1つのアプリは1つのことだけを完璧に行うに従い、疎結合なエコシステムを形成する。
- **細胞群 (Micro PWA Apps)**: 1アプリ1機能に徹底的に特化したツール群Brain, Habit, Pulse等
- **神経系 / クライアントOS (Posimai Veil)**: 各アプリへのアクセスを統合し、デスクトップ環境における「個人のコマンドセンターOS層」として機能する。単なるPWAランチャー认知的シェルターを超え、将来的にはElectronベースでシステムトレイ常駐・グローバルクイックキャプチャ・パーソナルアナリティクス・オフライン同期を備える。デスクトップ環境における「信頼のアンカー」となる。
- **Invisible Auth摩擦ゼロの回遊**: 全PWAを `posimai.soar-enrich.com/apps/*` の共通ドメイン下に配置し、First-party Cookie / LocalStorageを共有。別アプリへ飛んでもログイン・決済の壁を生じさせない。※前提条件ワイルドカードDNS `*.posimai.soar-enrich.com` の設定およびCookie方式への移行完了が必須。現在Eiji氏への最優先依頼タスク。
- **信頼のアンカーTier 1**: Flutter製の日本酒アプリ等をApp Storeに置くことで、権威性とトラスト証明書として機能させ、新規流入をPWA経済圏へ誘引する。
---
## 3. 販売構造Invisible Store透明なストア
トップに「ストア画面」を置くことで生じる認知負荷とコンバージョンの低下を防ぐと同時に、「信頼されない」というワンコイン・インディーズのリスクを構造で解決する。
- **入り口は特化型LPB案**: 流入先は各製品Ponshu Room等の課題解決に100%特化したページとする。
- **品質保証の透かし**: LPのフッターやヘッダーに「Posimai Standard」バッジ広告・サブスクなし、データ主権の明示を置き、巨大な信用基盤の証とする。
- **サンクスページでのクロスセル**: 決済完了画面にて、他のアプリを含む「Posimai Store」を展開し、信頼を獲得したユーザーに対してのみ回遊クロスセルを促す。
---
## 4. マネタイズとUXハイブリッド・クレジットと優雅な機能後退
AI等の変動コストが発生するアプリにおける「買い切りの赤字リスク」と「サブスクの摩擦」を調和させる究極のソリューション。
- **初期パッケージ化**: 基本機能の買い切り価格(例: 500円に「標準ユーザーが数年消費しないほど十分なAI処理枠例: 500スキャン分」を同梱。
- **使い切った後の従量課金**: 追加枠が必要なヘビーユーザーにのみ都度課金(トークン)を促す。
- **UXの魔法メタファー**: クレジットの残数を「チケット」や「スキャン回数」と呼ばず、「AIセラーの拡張領域」など独自のポジティブな概念に置き換える。
- **不可視化**: 残数は設定の最深部に隠し、普段の体験では一切意識させない。枯渇直前にのみ情緒的な表現のプログレスバーで知らせる。
- **優雅な機能後退 (Graceful Degradation)**: AI枠を使い切っても絶対にアプリをロックエラー表示しない。カメラボタンが自動解析モードから「手作業の手動入力モード」へシームレスかつ優雅にダウングレードするだけで、ユーザーは永久に無課金でもデータを記録し続けられる。

View File

@ -0,0 +1,142 @@
# PostgreSQL MCP セットアップガイド
**目的:** Claude Code から VPS の PostgreSQL 16posimai_brain DBに直接接続し、スキーマ確認・データ参照を自然言語で行えるようにする。
---
## アーキテクチャ
```
Windows (Claude Code)
↓ SSH トンネルTailscale 経由)
VPS (posimai-lab.tail72e846.ts.net)
↓ Docker 内部ネットワーク
PostgreSQL 16 コンテナ
→ DB: posimai_brain
```
---
## ステップ 1: 読み取り専用ユーザーの作成VPS で一度だけ実行)
VPS に SSH ログイン後、PostgreSQL コンテナに接続して実行する。
```bash
# VPS SSH ログイン
ssh <user>@posimai-lab.tail72e846.ts.net
# PostgreSQL コンテナに接続
docker exec -it <postgres_container_name> psql -U postgres
# 読み取り専用ユーザー作成
CREATE USER posimai_readonly WITH PASSWORD 'CHOOSE_STRONG_PASSWORD';
GRANT CONNECT ON DATABASE posimai_brain TO posimai_readonly;
\c posimai_brain
GRANT USAGE ON SCHEMA public TO posimai_readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO posimai_readonly;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO posimai_readonly;
# 確認
\du posimai_readonly
\q
```
**注意:** パスワードは安全なランダム文字列を設定すること。このユーザーは SELECT のみ可能INSERT/UPDATE/DELETE 不可)。
---
## ステップ 2: SSH トンネルの設定Claude Code 起動前)
Claude Code セッションを開始する前に、以下のコマンドでトンネルを張る。
```bash
# バックグラウンドでトンネルを張る(セッション維持)
ssh -N -L 5432:localhost:5432 <user>@posimai-lab.tail72e846.ts.net &
# または WSL2 / PowerShell で常時実行
# Windows の場合は Windows Terminal で別タブで実行しておく
```
**VPS の PostgreSQL コンテナが localhost:5432 を公開しているか確認:**
```bash
ssh <user>@posimai-lab.tail72e846.ts.net "docker ps | grep postgres"
# ポートが 0.0.0.0:5432 または 127.0.0.1:5432 で LISTEN していることを確認
```
---
## ステップ 3: .claude/settings.json への MCP 設定
`posimai-project/.claude/settings.json``mcpServers` セクションを追加済み。
**パスワードのみ実際の値に変更すること(このファイルは git 管理外)。**
```json
{
"mcpServers": {
"postgres": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-postgres",
"postgresql://posimai_readonly:CHANGE_ME@localhost:5432/posimai_brain"
]
}
}
}
```
---
## 使用例(設定完了後)
Claude Code のセッション内で以下のような質問が自然言語でできるようになる:
```
# スキーマ確認
「posimai_brain にどんなテーブルがある?」
# カラム確認
「users テーブルの構造を教えて」
# データ確認(読み取りのみ)
「habit テーブルに最近登録された習慣を5件見せて」
# デバッグ支援
「feed テーブルで unread_count が NULL のレコードはいくつある?」
```
---
## セキュリティ注意事項
| 項目 | 対応 |
|------|------|
| 認証情報の保管 | `.claude/settings.json`git 管理外)のみに記載 |
| アクセス制限 | Tailscale VPN 経由のみ(公開ポートなし)|
| 権限 | SELECT のみDDL・DML 不可)|
| パスワード | 強力なランダム文字列を設定 |
| CLAUDE.md | DB スキーマ変更は mai 確認必須ルールを維持 |
---
## コンテナ名の確認方法
VPS 上で以下を実行:
```bash
docker ps --format "table {{.Names}}\t{{.Ports}}" | grep -i postgres
```
`deploy-server.sh` または `docker-compose.yml` でコンテナ名を確認することもできる。
---
## トラブルシューティング
**接続できない場合:**
1. SSH トンネルが起動しているか確認: `ss -tlnp | grep 5432`
2. VPS PostgreSQL コンテナが起動しているか確認
3. ポートフォワード先が正しいか確認Docker ネットワーク設定による)
**MCP サーバーが起動しない場合:**
1. `npx -y @modelcontextprotocol/server-postgres --help` が動作するか確認
2. Node.js / npm がインストールされているか確認

View File

@ -1,9 +1,11 @@
{
"name": "posimai-root",
"private": true,
"type": "module",
"scripts": {
"deploy": "git push gitea main && git push github main",
"deploy:dev": "bash scripts/deploy-dev.sh",
"deploy:vps": "bash deploy-server.sh"
"deploy:vps": "bash deploy-server.sh",
"check:registrations": "node scripts/check-registrations.js"
}
}

1
posimai-atlas Submodule

@ -0,0 +1 @@
Subproject commit 09dd315c5ffbc8e7faf49e01cdf700cd9d7ae4f7

1
posimai-brief Submodule

@ -0,0 +1 @@
Subproject commit 1337c280b07a76ca54b2f5438f4f4672410378d2

855
posimai-dev/package-lock.json generated Normal file
View File

@ -0,0 +1,855 @@
{
"name": "posimai-dev",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "posimai-dev",
"version": "0.1.0",
"dependencies": {
"express": "4.19.2",
"node-pty": "1.0.0",
"ws": "8.18.0"
}
},
"node_modules/accepts": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
"license": "MIT",
"dependencies": {
"mime-types": "~2.1.34",
"negotiator": "0.6.3"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
"license": "MIT"
},
"node_modules/body-parser": {
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
"license": "MIT",
"dependencies": {
"bytes": "3.1.2",
"content-type": "~1.0.5",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.11.0",
"raw-body": "2.5.2",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/bytes": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/call-bound": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
"get-intrinsic": "^1.3.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/content-disposition": {
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
"license": "MIT",
"dependencies": {
"safe-buffer": "5.2.1"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/content-type": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
"license": "MIT"
},
"node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/destroy": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
"license": "MIT",
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
"es-errors": "^1.3.0",
"gopd": "^1.2.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
"license": "MIT"
},
"node_modules/encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-object-atoms": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
"license": "MIT"
},
"node_modules/etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/express": {
"version": "4.19.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
"integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
"license": "MIT",
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
"body-parser": "1.20.2",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
"cookie": "0.6.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "1.2.0",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.7",
"qs": "6.11.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.2.1",
"send": "0.18.0",
"serve-static": "1.15.0",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
},
"engines": {
"node": ">= 0.10.0"
}
},
"node_modules/finalhandler": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
"integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
"license": "MIT",
"dependencies": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"statuses": "2.0.1",
"unpipe": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
"es-define-property": "^1.0.1",
"es-errors": "^1.3.0",
"es-object-atoms": "^1.1.1",
"function-bind": "^1.1.2",
"get-proto": "^1.0.1",
"gopd": "^1.2.0",
"has-symbols": "^1.1.0",
"hasown": "^2.0.2",
"math-intrinsics": "^1.1.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
"license": "MIT",
"dependencies": {
"dunder-proto": "^1.0.1",
"es-object-atoms": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-symbols": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/http-errors": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
"license": "MIT",
"dependencies": {
"depd": "2.0.0",
"inherits": "2.0.4",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"toidentifier": "1.0.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"license": "ISC"
},
"node_modules/ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
"license": "MIT",
"engines": {
"node": ">= 0.10"
}
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==",
"license": "MIT"
},
"node_modules/methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"license": "MIT",
"bin": {
"mime": "cli.js"
},
"engines": {
"node": ">=4"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"license": "MIT",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"license": "MIT"
},
"node_modules/nan": {
"version": "2.26.2",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.26.2.tgz",
"integrity": "sha512-0tTvBTYkt3tdGw22nrAy50x7gpbGCCFH3AFcyS5WiUu7Eu4vWlri1woE6qHBSfy11vksDqkiwjOnlR7WV8G1Hw==",
"license": "MIT"
},
"node_modules/negotiator": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/node-pty": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.0.0.tgz",
"integrity": "sha512-wtBMWWS7dFZm/VgqElrTvtfMq4GzJ6+edFI0Y0zyzygUSZMgZdraDUMUhCIvkjhJjme15qWmbyJbtAx4ot4uZA==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"nan": "^2.17.0"
}
},
"node_modules/object-inspect": {
"version": "1.13.4",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/on-finished": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
"license": "MIT",
"dependencies": {
"ee-first": "1.1.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==",
"license": "MIT"
},
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
"license": "MIT",
"dependencies": {
"forwarded": "0.2.0",
"ipaddr.js": "1.9.1"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/qs": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.0.4"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/raw-body": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
"license": "MIT",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"license": "MIT"
},
"node_modules/send": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
"integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
"license": "MIT",
"dependencies": {
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"mime": "1.6.0",
"ms": "2.1.3",
"on-finished": "2.4.1",
"range-parser": "~1.2.1",
"statuses": "2.0.1"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/send/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/serve-static": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
"integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
"license": "MIT",
"dependencies": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.18.0"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
"license": "ISC"
},
"node_modules/side-channel": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"object-inspect": "^1.13.3",
"side-channel-list": "^1.0.0",
"side-channel-map": "^1.0.1",
"side-channel-weakmap": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/side-channel-list": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"object-inspect": "^1.13.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/side-channel-map": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.2",
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.5",
"object-inspect": "^1.13.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/side-channel-weakmap": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
"license": "MIT",
"dependencies": {
"call-bound": "^1.0.2",
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.5",
"object-inspect": "^1.13.3",
"side-channel-map": "^1.0.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/toidentifier": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
"license": "MIT",
"engines": {
"node": ">=0.6"
}
},
"node_modules/type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"license": "MIT",
"dependencies": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
"license": "MIT",
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/ws": {
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
}
}
}

@ -1 +1 @@
Subproject commit 26cacc8cec858ba8d71124ed93440f8601979d6e
Subproject commit 151eb32e468ccb79a6f6373aedb0ce62ae993997

View File

@ -3,26 +3,12 @@
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"tauri": "tauri",
"tauri:dev": "tauri dev",
"tauri:build": "tauri build"
},
"dependencies": {
"@tauri-apps/api": "^2.3.0",
"@tauri-apps/plugin-dialog": "^2.2.0",
"@tauri-apps/plugin-fs": "^2.2.0",
"@tauri-apps/plugin-shell": "^2.2.0",
"react": "^19.0.0",
"react-dom": "^19.0.0"
"dev": "tauri dev",
"build": "tauri build"
},
"dependencies": {},
"devDependencies": {
"@tauri-apps/cli": "^2.3.1",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@vitejs/plugin-react": "^4.3.4",
"typescript": "^5.7.2",
"vite": "^6.0.7"
"@tauri-apps/cli": "^2.3.1"
}
}

5162
posimai-guard-app/src-tauri/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -20,6 +20,11 @@ tauri-plugin-shell = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
[package.metadata.bundle]
name = "Posimai Guard"
identifier = "com.posimai.guard"
icon = ["icons/icon.ico", "icons/icon.icns", "icons/icon.png"]
[profile.release]
panic = "abort"
codegen-units = 1

View File

@ -0,0 +1,18 @@
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "Posimai Guard desktop capabilities",
"windows": ["main"],
"permissions": [
"core:default",
"dialog:allow-open",
"dialog:allow-save",
"dialog:allow-message",
"dialog:allow-ask",
"fs:allow-read-text-file",
"fs:allow-write-text-file",
"fs:allow-read-dir",
"fs:allow-exists",
"shell:allow-open"
]
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"default":{"identifier":"default","description":"Posimai Guard desktop capabilities","local":true,"windows":["main"],"permissions":["core:default","dialog:allow-open","dialog:allow-save","dialog:allow-message","dialog:allow-ask","fs:allow-read-text-file","fs:allow-write-text-file","fs:allow-read-dir","fs:allow-exists","shell:allow-open"]}}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 664 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 347 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 761 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 852 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 B

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
<background android:drawable="@color/ic_launcher_background"/>
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 515 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 488 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 901 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 717 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 566 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 904 B

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#fff</color>
</resources>

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 419 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 388 B

View File

@ -3,10 +3,8 @@
"version": "0.1.0",
"identifier": "com.posimai.guard",
"build": {
"frontendDist": "../dist",
"devUrl": "http://localhost:1420",
"beforeDevCommand": "npm run dev",
"beforeBuildCommand": "npm run build"
"frontendDist": "https://guard.posimai.soar-enrich.com",
"devUrl": "https://guard.posimai.soar-enrich.com"
},
"app": {
"windows": [
@ -37,9 +35,5 @@
"icons/icon.ico"
]
},
"plugins": {
"shell": {
"open": true
}
}
"plugins": {}
}

View File

@ -2,7 +2,7 @@
"name": "posimai-guard",
"displayName": "Guard — AIコードセキュリティスキャナー",
"description": "AIが生成したコードのセキュリティリスクをワークスペース内で直接検出します",
"version": "0.1.0",
"version": "0.2.0",
"publisher": "posimai",
"engines": { "vscode": "^1.85.0" },
"categories": ["Linters", "Other"],
@ -26,6 +26,14 @@
"command": "guard.setApiKeys",
"title": "Guard: APIキーを設定"
},
{
"command": "guard.scanGitHistory",
"title": "Guard: git履歴のシークレットをスキャン"
},
{
"command": "guard.scanDeps",
"title": "Guard: 依存関係CVE・ライセンスをスキャン"
},
{
"command": "guard.clearDiagnostics",
"title": "Guard: 診断をクリア"
@ -41,6 +49,8 @@
"commandPalette": [
{ "command": "guard.scanWorkspace" },
{ "command": "guard.scanFile" },
{ "command": "guard.scanGitHistory" },
{ "command": "guard.scanDeps" },
{ "command": "guard.setApiKeys" },
{ "command": "guard.clearDiagnostics" }
]

View File

@ -0,0 +1,163 @@
import * as vscode from 'vscode';
import * as fs from 'fs';
import * as path from 'path';
import * as https from 'https';
import { ResultsPanel } from '../ui/resultsPanel';
import type { ScanIssue } from '../scanner/prompt';
const COPYLEFT = new Set([
'GPL-2.0', 'GPL-3.0', 'GPL-2.0-only', 'GPL-3.0-only',
'AGPL-3.0', 'AGPL-3.0-only', 'LGPL-2.1', 'LGPL-3.0',
]);
function httpsPost(url: string, body: string): Promise<string> {
return new Promise((resolve, reject) => {
const parsed = new URL(url);
const req = https.request(
{ hostname: parsed.hostname, path: parsed.pathname, method: 'POST', headers: { 'Content-Type': 'application/json' } },
res => {
const chunks: Buffer[] = [];
res.on('data', d => chunks.push(d));
res.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));
},
);
req.on('error', reject);
req.write(body);
req.end();
});
}
function httpsGet(url: string): Promise<string> {
return new Promise((resolve, reject) => {
https.get(url, { headers: { Accept: 'application/json' } }, res => {
const chunks: Buffer[] = [];
res.on('data', d => chunks.push(d));
res.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));
}).on('error', reject);
});
}
function parseDeps(pkgJson: string): { name: string; version: string; isDev: boolean }[] {
const parsed = JSON.parse(pkgJson);
const prod = Object.entries(parsed.dependencies ?? {}).map(([name, ver]) => ({
name, version: String(ver).replace(/^[\^~>=<*]/, '').split(' ')[0], isDev: false,
}));
const dev = Object.entries(parsed.devDependencies ?? {}).map(([name, ver]) => ({
name, version: String(ver).replace(/^[\^~>=<*]/, '').split(' ')[0], isDev: true,
}));
return [...prod, ...dev];
}
export async function scanDeps(
context: vscode.ExtensionContext,
): Promise<void> {
const folders = vscode.workspace.workspaceFolders;
if (!folders || folders.length === 0) {
vscode.window.showWarningMessage('Guard: ワークスペースが開かれていません');
return;
}
const pkgPath = path.join(folders[0].uri.fsPath, 'package.json');
if (!fs.existsSync(pkgPath)) {
vscode.window.showWarningMessage('Guard: package.json が見つかりません');
return;
}
const pkgJson = fs.readFileSync(pkgPath, 'utf-8');
let deps: ReturnType<typeof parseDeps>;
try {
deps = parseDeps(pkgJson);
} catch {
vscode.window.showErrorMessage('Guard: package.json の解析に失敗しました');
return;
}
await vscode.window.withProgress(
{ location: vscode.ProgressLocation.Notification, title: 'Guard: 依存関係をスキャン中...', cancellable: false },
async progress => {
const issues: ScanIssue[] = [];
// ── CVE (OSV.dev) ──
progress.report({ message: `CVE照合中 (${deps.length} パッケージ)...` });
try {
const CHUNK = 200;
for (let i = 0; i < deps.length; i += CHUNK) {
const chunk = deps.slice(i, i + CHUNK);
const body = JSON.stringify({
queries: chunk.map(d => ({ package: { name: d.name, ecosystem: 'npm' }, version: d.version })),
});
const raw = await httpsPost('https://api.osv.dev/v1/querybatch', body);
const data = JSON.parse(raw) as { results: { vulns?: { id: string; summary?: string; aliases?: string[] }[] }[] };
for (let j = 0; j < chunk.length; j++) {
const vulns = data.results[j]?.vulns ?? [];
for (const v of vulns) {
const cve = v.aliases?.find(a => a.startsWith('CVE-')) ?? v.id;
issues.push({
severity: 'danger',
title: `CVE: ${chunk[j].name}`,
description: `${v.summary ?? '既知の脆弱性'} (${cve})`,
file: 'package.json',
line: null,
fix: `npm update ${chunk[j].name}`,
});
}
}
}
} catch {
// OSV.dev 失敗は無視
}
// ── ライセンス ──
progress.report({ message: 'ライセンス確認中...' });
const prodDeps = deps.filter(d => !d.isDev).slice(0, 40);
await Promise.allSettled(
prodDeps.map(async d => {
try {
const raw = await httpsGet(`https://registry.npmjs.org/${d.name}/latest`);
const data = JSON.parse(raw) as { license?: string; deprecated?: string };
const license = data.license ?? 'UNKNOWN';
if (data.deprecated) {
issues.push({
severity: 'warning',
title: `非推奨: ${d.name}`,
description: String(data.deprecated).slice(0, 120),
file: 'package.json', line: null,
fix: `npm install <代替パッケージ> # npm info ${d.name} を確認`,
});
} else if (!license || license === 'UNKNOWN' || license === 'UNLICENSED') {
issues.push({
severity: 'info',
title: `ライセンス不明: ${d.name}`,
description: '商用利用前にライセンス確認が必要です',
file: 'package.json', line: null, fix: null,
});
} else {
const spdx = license.replace(/[()]/g, '').split(/\s+(?:OR|AND)\s+/);
if (spdx.some(l => COPYLEFT.has(l.trim()))) {
issues.push({
severity: 'warning',
title: `コピーレフト: ${d.name}`,
description: `${license} ライセンス。商用クローズドソース製品への組み込みは法的確認が必要です`,
file: 'package.json', line: null, fix: null,
});
}
}
} catch { /* skip */ }
}),
);
ResultsPanel.show(context, issues, 'package.json (CVE + License)');
const cveCount = issues.filter(i => i.title.startsWith('CVE:')).length;
const licenseCount = issues.length - cveCount;
const msg = `Guard: CVE ${cveCount}件 / ライセンス問題 ${licenseCount}`;
if (cveCount > 0) {
vscode.window.showErrorMessage(msg);
} else if (licenseCount > 0) {
vscode.window.showWarningMessage(msg);
} else {
vscode.window.showInformationMessage('Guard: 依存関係に問題は見つかりませんでした');
}
},
);
}

View File

@ -5,6 +5,8 @@ import { scanWithClaude } from '../scanner/claudeClient';
import { issuesToDiagnostics } from '../ui/diagnostics';
import { ResultsPanel } from '../ui/resultsPanel';
import { ScanIssue } from '../scanner/prompt';
// Rule engine: deterministic scan — no API key required
import { runRuleEngine } from '../../../posimai-guard/src/lib/ruleEngine';
export async function scanFile(
context: vscode.ExtensionContext,
@ -16,18 +18,6 @@ export async function scanFile(
return;
}
const geminiKey = await context.secrets.get('guard.geminiKey');
if (!geminiKey) {
const action = await vscode.window.showErrorMessage(
'Guard: Gemini API キーが未設定です',
'APIキーを設定',
);
if (action === 'APIキーを設定') {
await vscode.commands.executeCommand('guard.setApiKeys');
}
return;
}
const config = vscode.workspace.getConfiguration('guard');
const model: string = config.get('model') ?? 'gemini';
@ -41,21 +31,33 @@ export async function scanFile(
cancellable: false,
},
async () => {
let issues: ScanIssue[] = [];
// ── Step 1: Rule engine (instant, no API key) ──────────────
const ruleIssues = runRuleEngine(files) as unknown as ScanIssue[];
const seen = new Set(ruleIssues.map(i => `${i.file}::${i.title}`));
let issues: ScanIssue[] = [...ruleIssues];
try {
issues = await scanWithGemini(files, geminiKey);
} catch (err) {
vscode.window.showErrorMessage(`Guard: Gemini スキャンに失敗しました: ${String(err)}`);
return;
// ── Step 2: Gemini (optional) ─────────────────────────────
const geminiKey = await context.secrets.get('guard.geminiKey');
if (geminiKey && (model === 'gemini' || model === 'both')) {
try {
const geminiIssues = await scanWithGemini(files, geminiKey);
for (const gi of geminiIssues) {
if (!seen.has(`${gi.file}::${gi.title}`)) {
issues.push(gi);
seen.add(`${gi.file}::${gi.title}`);
}
}
} catch {
// optional
}
}
// ── Step 3: Claude (optional) ─────────────────────────────
if (model === 'claude' || model === 'both') {
const claudeKey = await context.secrets.get('guard.claudeKey');
if (claudeKey) {
try {
const claudeIssues = await scanWithClaude(files, claudeKey);
const seen = new Set(issues.map(i => `${i.file}::${i.title}`));
for (const ci of claudeIssues) {
if (!seen.has(`${ci.file}::${ci.title}`)) {
issues.push(ci);

View File

@ -0,0 +1,142 @@
import * as vscode from 'vscode';
import * as cp from 'child_process';
import * as path from 'path';
import { ResultsPanel } from '../ui/resultsPanel';
import { issuesToDiagnostics } from '../ui/diagnostics';
import type { ScanIssue } from '../scanner/prompt';
// シークレットパターン
const SECRET_PATTERNS: { re: RegExp; title: string }[] = [
{ re: /(?:api[_-]?key|apikey)\s*[:=]\s*['"]?([A-Za-z0-9_\-]{20,})['"]?/i, title: 'APIキーがコミットに含まれている' },
{ re: /(?:password|passwd|secret|token)\s*[:=]\s*['"]?([^\s'"]{8,})['"]?/i, title: 'パスワード/トークンがコミットに含まれている' },
{ re: /sk-[a-zA-Z0-9]{48}/, title: 'OpenAI APIキーがコミットに含まれている' },
{ re: /sk-ant-[a-zA-Z0-9\-]{40,}/, title: 'Anthropic APIキーがコミットに含まれている' },
{ re: /AIza[0-9A-Za-z_\-]{35}/, title: 'Google APIキーがコミットに含まれている' },
{ re: /AKIA[0-9A-Z]{16}/, title: 'AWS アクセスキーがコミットに含まれている' },
{ re: /gh[pousr]_[A-Za-z0-9_]{36}/, title: 'GitHub トークンがコミットに含まれている' },
{ re: /-----BEGIN (?:RSA|EC|DSA|OPENSSH) PRIVATE KEY-----/, title: '秘密鍵がコミットに含まれている' },
{ re: /(?:mongodb|postgres|mysql|redis):\/\/[^\s'"]+:[^\s'"@]+@/, title: 'DB接続文字列認証情報付きがコミットに含まれている' },
];
interface GitFinding {
commit: string;
file: string;
line: string;
pattern: string;
snippet: string;
}
function spawnGit(args: string[], cwd: string): string {
try {
const result = cp.spawnSync('git', args, { cwd, encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024, timeout: 30000 });
return result.stdout ?? '';
} catch {
return '';
}
}
function scanDiff(diff: string, commitHash: string): GitFinding[] {
const findings: GitFinding[] = [];
let currentFile = '';
for (const rawLine of diff.split('\n')) {
if (rawLine.startsWith('+++ b/')) {
currentFile = rawLine.slice(6).trim();
continue;
}
if (!rawLine.startsWith('+') || rawLine.startsWith('+++')) continue;
const line = rawLine.slice(1);
for (const { re, title } of SECRET_PATTERNS) {
if (re.test(line)) {
// 実際の値をマスクして表示
const masked = line.replace(re, (m) => m.slice(0, 6) + '****');
findings.push({
commit: commitHash,
file: currentFile,
line: masked.trim().slice(0, 120),
pattern: title,
snippet: masked.trim(),
});
break; // 1行につき1パターンまで
}
}
}
return findings;
}
export async function scanGitHistory(
context: vscode.ExtensionContext,
collection: vscode.DiagnosticCollection,
): Promise<void> {
const folders = vscode.workspace.workspaceFolders;
if (!folders || folders.length === 0) {
vscode.window.showWarningMessage('Guard: ワークスペースが開かれていません');
return;
}
const root = folders[0].uri.fsPath;
// gitリポジトリ確認
const gitCheck = spawnGit(['rev-parse', '--git-dir'], root);
if (!gitCheck.trim()) {
vscode.window.showWarningMessage('Guard: gitリポジトリが見つかりません');
return;
}
await vscode.window.withProgress(
{ location: vscode.ProgressLocation.Notification, title: 'Guard: git履歴をスキャン中...', cancellable: false },
async progress => {
progress.report({ message: 'コミット一覧を取得中...' });
// 直近100コミットのハッシュ取得
const logOut = spawnGit(['log', '--oneline', '-100', '--format=%H'], root);
const commits = logOut.split('\n').map(l => l.trim()).filter(Boolean);
if (commits.length === 0) {
vscode.window.showInformationMessage('Guard: コミット履歴がありません');
return;
}
const allFindings: GitFinding[] = [];
for (let i = 0; i < commits.length; i++) {
const hash = commits[i];
progress.report({ message: `${i + 1}/${commits.length} コミットを検査中...`, increment: 100 / commits.length });
// 追加行のみを対象にdiff取得
const diff = spawnGit(['show', '--diff-filter=A', '-U0', hash], root);
const found = scanDiff(diff, hash.slice(0, 8));
allFindings.push(...found);
}
// 重複除去(同じファイル×パターン)
const seen = new Set<string>();
const unique = allFindings.filter(f => {
const key = `${f.file}::${f.pattern}`;
if (seen.has(key)) return false;
seen.add(key);
return true;
});
// ScanIssue に変換
const issues: ScanIssue[] = unique.map(f => ({
severity: 'danger' as const,
title: f.pattern,
description: `コミット ${f.commit}${f.file} に含まれています。git履歴から完全に削除してくださいBFG Repo-Cleaner推奨`,
file: f.file,
line: null,
fix: 'npx bfg --delete-files <filename> # または git filter-repo を使用して履歴から削除',
}));
issuesToDiagnostics(issues, collection);
ResultsPanel.show(context, issues, `git履歴 (${commits.length}コミット)`);
if (issues.length === 0) {
vscode.window.showInformationMessage('Guard: git履歴にシークレットは検出されませんでした');
} else {
vscode.window.showErrorMessage(`Guard: git履歴に ${issues.length} 件のシークレットが含まれています`);
}
},
);
}

View File

@ -5,6 +5,8 @@ import { scanWithClaude } from '../scanner/claudeClient';
import { issuesToDiagnostics } from '../ui/diagnostics';
import { ResultsPanel } from '../ui/resultsPanel';
import { ScanIssue } from '../scanner/prompt';
// Rule engine: deterministic scan — no API key required
import { runRuleEngine } from '../../../posimai-guard/src/lib/ruleEngine';
export async function scanWorkspace(
context: vscode.ExtensionContext,
@ -16,18 +18,6 @@ export async function scanWorkspace(
return;
}
const geminiKey = await context.secrets.get('guard.geminiKey');
if (!geminiKey) {
const action = await vscode.window.showErrorMessage(
'Guard: Gemini API キーが未設定です',
'APIキーを設定',
);
if (action === 'APIキーを設定') {
await vscode.commands.executeCommand('guard.setApiKeys');
}
return;
}
const config = vscode.workspace.getConfiguration('guard');
const maxFiles: number = config.get('maxFiles') ?? 80;
const model: string = config.get('model') ?? 'gemini';
@ -48,24 +38,39 @@ export async function scanWorkspace(
}
const sourceName = folders[0].name;
let issues: ScanIssue[] = [];
try {
progress.report({ message: `Gemini でスキャン中... (${files.length} ファイル)` });
issues = await scanWithGemini(files, geminiKey);
} catch (err) {
vscode.window.showErrorMessage(`Guard: Gemini スキャンに失敗しました: ${String(err)}`);
return;
// ── Step 1: Rule engine (instant, no API key) ──────────────
progress.report({ message: `静的ルール解析中... (${files.length} ファイル)` });
const ruleIssues = runRuleEngine(files) as unknown as ScanIssue[];
// Show rule engine results immediately
const seen = new Set(ruleIssues.map(i => `${i.file}::${i.title}`));
let issues: ScanIssue[] = [...ruleIssues];
// ── Step 2: Gemini (optional — deeper semantic analysis) ───
const geminiKey = await context.secrets.get('guard.geminiKey');
if (geminiKey && (model === 'gemini' || model === 'both')) {
try {
progress.report({ message: 'Gemini でセマンティック解析中...' });
const geminiIssues = await scanWithGemini(files, geminiKey);
for (const gi of geminiIssues) {
if (!seen.has(`${gi.file}::${gi.title}`)) {
issues.push(gi);
seen.add(`${gi.file}::${gi.title}`);
}
}
} catch {
// Gemini is optional — silent fail
}
}
// ── Step 3: Claude (optional) ─────────────────────────────
if (model === 'claude' || model === 'both') {
const claudeKey = await context.secrets.get('guard.claudeKey');
if (claudeKey) {
try {
progress.report({ message: 'Claude でスキャン中...' });
progress.report({ message: 'Claude でセマンティック解析中...' });
const claudeIssues = await scanWithClaude(files, claudeKey);
// Merge: dedup by file + title
const seen = new Set(issues.map(i => `${i.file}::${i.title}`));
for (const ci of claudeIssues) {
if (!seen.has(`${ci.file}::${ci.title}`)) {
issues.push(ci);
@ -83,7 +88,10 @@ export async function scanWorkspace(
const danger = issues.filter(i => i.severity === 'danger').length;
const warning = issues.filter(i => i.severity === 'warning').length;
const msg = `Guard: ${issues.length} 件検出 (危険 ${danger} / 警告 ${warning})`;
const ruleCount = ruleIssues.length;
const aiCount = issues.length - ruleCount;
const aiNote = aiCount > 0 ? ` (+AI ${aiCount})` : '';
const msg = `Guard: ${issues.length} 件検出 (危険 ${danger} / 警告 ${warning})${aiNote}`;
if (danger > 0) {
vscode.window.showErrorMessage(msg);

View File

@ -1,6 +1,8 @@
import * as vscode from 'vscode';
import { scanWorkspace } from './commands/scanWorkspace';
import { scanFile } from './commands/scanFile';
import { scanGitHistory } from './commands/scanGitHistory';
import { scanDeps } from './commands/scanDeps';
let diagnosticCollection: vscode.DiagnosticCollection;
@ -11,7 +13,7 @@ export function activate(context: vscode.ExtensionContext): void {
// Status bar item
const statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100);
statusBar.text = '$(shield) Guard';
statusBar.tooltip = 'Guard: ワークスペースをスキャン';
statusBar.tooltip = 'Guard: ワークスペースをスキャンAPIキー不要';
statusBar.command = 'guard.scanWorkspace';
statusBar.show();
context.subscriptions.push(statusBar);
@ -63,6 +65,14 @@ export function activate(context: vscode.ExtensionContext): void {
}
}),
vscode.commands.registerCommand('guard.scanGitHistory', () =>
scanGitHistory(context, diagnosticCollection),
),
vscode.commands.registerCommand('guard.scanDeps', () =>
scanDeps(context),
),
vscode.commands.registerCommand('guard.clearDiagnostics', () => {
diagnosticCollection.clear();
vscode.window.showInformationMessage('Guard: 診断をクリアしました');

View File

@ -4,7 +4,6 @@
"module": "commonjs",
"lib": ["ES2020"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,

@ -1 +1 @@
Subproject commit 40dace3dddf681af4f7f3bb54717a6900441e7e4
Subproject commit abd706a9e63d4c114a947551ff9996d4ff7ad58f

1
posimai-roadmap Submodule

@ -0,0 +1 @@
Subproject commit 06d36bf4a203f624de03458e910cd99232dcc993

1
posimai-station-app Submodule

@ -0,0 +1 @@
Subproject commit 9d0ab6043e80cdb011d63cb219b540c1b941d2c3

@ -1 +1 @@
Subproject commit 0533f6fe8cc3a48f58b8346a6e9cada68567e530
Subproject commit 59c8dfe7230048c22aff701e9f638c87eb52a6c6

1
posimai-veil Submodule

@ -0,0 +1 @@
Subproject commit 23199824456d981f6aaede27cafd25868cef8532

View File

@ -0,0 +1,105 @@
#!/usr/bin/env node
/**
* check-registrations.js
* 新規アプリがすべての登録ファイルに存在するかを一括検証する
* 使い方: node scripts/check-registrations.js [app-id]
* app-id を省略すると全アプリを一括チェック
*/
import { readFileSync, existsSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const ROOT = join(__dirname, '..');
// 登録が必要なファイル
const TARGETS = [
{
label: 'projects.json',
path: 'posimai-dashboard/src/data/projects.json',
check: (content, id) => {
const data = JSON.parse(content);
return data.projects.some(p => p.id === id);
},
},
{
label: 'apps/page.tsx (APP_CATEGORIES)',
path: 'posimai-dashboard/src/app/apps/page.tsx',
check: (content, id) => content.includes(`"${id}"`),
},
{
label: 'ecosystem/page.tsx (NODES)',
path: 'posimai-dashboard/src/app/ecosystem/page.tsx',
check: (content, id) => {
// id は "posimai-xxx" → node id は "xxx"
const shortId = id.replace(/^posimai-/, '');
return content.includes(`id: "${shortId}"`) || content.includes(`id: "${id}"`);
},
},
{
label: 'roadmap.json',
path: 'posimai-roadmap/roadmap.json',
check: (content, id) => {
const data = JSON.parse(content);
return (data.apps ?? []).concat(data.other ?? []).some(a => a.id === id)
|| content.includes(`"id": "${id}"`);
},
},
{
label: 'timeline/page.tsx (直近イベント)',
path: 'posimai-dashboard/src/app/timeline/page.tsx',
check: (content, id) => content.includes(`"${id}"`),
},
{
label: 'atlas.json',
path: 'posimai-atlas/atlas.json',
check: (content, id) => {
// atlas は posimai-apps ノードにまとめて記載する設計なので
// short id (guard) または full id (posimai-guard) が含まれればOK
const shortId = id.replace(/^posimai-/, '');
return content.includes(id) || content.includes(shortId);
},
},
];
// チェック対象のアプリID引数 or 全 posimai-* ディレクトリ)
function getAppIds() {
const arg = process.argv[2];
if (arg) return [arg];
// projects.json から全IDを取得
const pjson = JSON.parse(readFileSync(join(ROOT, 'posimai-dashboard/src/data/projects.json'), 'utf8'));
return pjson.projects.map(p => p.id);
}
function run() {
const appIds = getAppIds();
let anyFail = false;
for (const id of appIds) {
const missing = [];
for (const target of TARGETS) {
const filePath = join(ROOT, target.path);
if (!existsSync(filePath)) { missing.push(`${target.label} (ファイル不在)`); continue; }
const content = readFileSync(filePath, 'utf8');
if (!target.check(content, id)) missing.push(target.label);
}
if (missing.length > 0) {
anyFail = true;
console.log(`\n[MISSING] ${id}`);
missing.forEach(m => console.log(` - ${m}`));
} else {
console.log(`[OK] ${id}`);
}
}
if (anyFail) {
console.log('\n登録が不足しているアプリがあります。CLAUDE.md の「アプリ追加・更新時」ルールを確認してください。');
process.exit(1);
} else {
console.log('\n全アプリの登録は完了しています。');
}
}
run();

Some files were not shown because too many files have changed in this diff Show More