codeply-cli 0.5.1 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/THIRD_PARTY_NOTICES.md +65 -63
- package/bin/codeply.js +1 -1
- package/lib/agent.mjs +133 -24
- package/lib/ai.js +268 -9
- package/lib/ai.mjs +6 -6
- package/lib/apply-patch.js +217 -217
- package/lib/arity-table.json +138 -138
- package/lib/arity.js +70 -70
- package/lib/auth.mjs +6 -6
- package/lib/bots.js +82 -17
- package/lib/chatgpt.js +282 -0
- package/lib/cloud-browser.mjs +143 -143
- package/lib/cloud.mjs +1186 -1144
- package/lib/commands.js +103 -103
- package/lib/config.js +83 -3
- package/lib/design-library/assemble-artifact.mjs +21 -21
- package/lib/design-library/cleanup.mjs +45 -45
- package/lib/design-library/index.json +22379 -22379
- package/lib/design-library/make-table-data.mjs +39 -39
- package/lib/diagnostics.js +192 -192
- package/lib/edit-engine.js +364 -364
- package/lib/github-agent.mjs +311 -311
- package/lib/history.js +75 -75
- package/lib/mail-watch.js +504 -0
- package/lib/mcp.js +258 -258
- package/lib/model-sync.js +114 -0
- package/lib/native-tools.mjs +16 -1
- package/lib/oauth-connectors.js +146 -24
- package/lib/permissions.js +109 -109
- package/lib/plugins.js +353 -353
- package/lib/publish.js +916 -0
- package/lib/reach.js +67 -0
- package/lib/server.mjs +457 -457
- package/lib/session-db.js +220 -220
- package/lib/share.js +168 -168
- package/lib/skills.js +2 -0
- package/lib/snapshot.js +146 -146
- package/lib/telemetry.js +102 -0
- package/lib/terminal-hints.js +154 -154
- package/lib/tools.mjs +316 -14
- package/lib/typecheck-worker.js +147 -147
- package/lib/web-tools.js +209 -158
- package/package.json +49 -49
- package/skills/SOURCE.md +58 -58
- package/skills/accessibility/SKILL.md +147 -147
- package/skills/agent-architecture-audit/SKILL.md +257 -257
- package/skills/agent-eval/SKILL.md +147 -147
- package/skills/agent-harness-construction/SKILL.md +74 -74
- package/skills/agent-introspection-debugging/SKILL.md +154 -154
- package/skills/agent-payment-x402/SKILL.md +225 -225
- package/skills/agent-self-evaluation/SKILL.md +182 -182
- package/skills/agent-self-evaluation/references/evaluation-criteria.md +71 -71
- package/skills/agent-self-evaluation/references/hook-integration.md +64 -64
- package/skills/agent-sort/SKILL.md +216 -216
- package/skills/agentic-engineering/SKILL.md +64 -64
- package/skills/agentic-os/SKILL.md +388 -388
- package/skills/ai-first-engineering/SKILL.md +52 -52
- package/skills/ai-regression-testing/SKILL.md +386 -386
- package/skills/android-clean-architecture/SKILL.md +340 -340
- package/skills/angular-developer/SKILL.md +155 -155
- package/skills/angular-developer/references/angular-animations.md +160 -160
- package/skills/angular-developer/references/angular-aria.md +410 -410
- package/skills/angular-developer/references/cli.md +86 -86
- package/skills/angular-developer/references/component-harnesses.md +59 -59
- package/skills/angular-developer/references/component-styling.md +91 -91
- package/skills/angular-developer/references/components.md +117 -117
- package/skills/angular-developer/references/creating-services.md +97 -97
- package/skills/angular-developer/references/data-resolvers.md +69 -69
- package/skills/angular-developer/references/define-routes.md +67 -67
- package/skills/angular-developer/references/defining-providers.md +72 -72
- package/skills/angular-developer/references/di-fundamentals.md +120 -120
- package/skills/angular-developer/references/e2e-testing.md +56 -56
- package/skills/angular-developer/references/effects.md +83 -83
- package/skills/angular-developer/references/hierarchical-injectors.md +43 -43
- package/skills/angular-developer/references/host-elements.md +80 -80
- package/skills/angular-developer/references/injection-context.md +63 -63
- package/skills/angular-developer/references/inputs.md +101 -101
- package/skills/angular-developer/references/linked-signal.md +59 -59
- package/skills/angular-developer/references/loading-strategies.md +61 -61
- package/skills/angular-developer/references/mcp.md +108 -108
- package/skills/angular-developer/references/navigate-to-routes.md +69 -69
- package/skills/angular-developer/references/outputs.md +86 -86
- package/skills/angular-developer/references/reactive-forms.md +122 -122
- package/skills/angular-developer/references/rendering-strategies.md +44 -44
- package/skills/angular-developer/references/resource.md +77 -77
- package/skills/angular-developer/references/route-animations.md +56 -56
- package/skills/angular-developer/references/route-guards.md +52 -52
- package/skills/angular-developer/references/router-lifecycle.md +45 -45
- package/skills/angular-developer/references/router-testing.md +87 -87
- package/skills/angular-developer/references/show-routes-with-outlets.md +68 -68
- package/skills/angular-developer/references/signal-forms.md +795 -795
- package/skills/angular-developer/references/signals-overview.md +94 -94
- package/skills/angular-developer/references/tailwind-css.md +69 -69
- package/skills/angular-developer/references/template-driven-forms.md +114 -114
- package/skills/angular-developer/references/testing-fundamentals.md +65 -65
- package/skills/api-connector-builder/SKILL.md +121 -121
- package/skills/api-design/SKILL.md +524 -524
- package/skills/architecture-decision-records/SKILL.md +180 -180
- package/skills/article-writing/SKILL.md +80 -80
- package/skills/automation-audit-ops/SKILL.md +143 -143
- package/skills/autonomous-agent-harness/SKILL.md +274 -274
- package/skills/autonomous-loops/SKILL.md +611 -611
- package/skills/backend-patterns/SKILL.md +562 -562
- package/skills/benchmark/SKILL.md +95 -95
- package/skills/benchmark-methodology/SKILL.md +191 -191
- package/skills/benchmark-optimization-loop/SKILL.md +71 -71
- package/skills/blender-motion-state-inspection/SKILL.md +165 -165
- package/skills/blueprint/SKILL.md +106 -106
- package/skills/brand-discovery/SKILL.md +145 -145
- package/skills/brand-discovery/references/10_purpose-why.md +40 -40
- package/skills/brand-discovery/references/20_positioning.md +44 -44
- package/skills/brand-discovery/references/30_audience-niche.md +52 -52
- package/skills/brand-discovery/references/40_personality-archetype.md +57 -57
- package/skills/brand-discovery/references/50_voice-tone.md +59 -59
- package/skills/brand-discovery/references/60_narrative-story.md +50 -50
- package/skills/brand-discovery/references/70_founder-tension.md +49 -49
- package/skills/brand-discovery/references/90_SYNTHESIS.md +133 -133
- package/skills/brand-voice/SKILL.md +98 -98
- package/skills/brand-voice/references/voice-profile-schema.md +55 -55
- package/skills/browser-qa/SKILL.md +105 -105
- package/skills/bun-runtime/SKILL.md +85 -85
- package/skills/canary-watch/SKILL.md +108 -108
- package/skills/carrier-relationship-management/SKILL.md +212 -212
- package/skills/cisco-ios-patterns/SKILL.md +164 -164
- package/skills/ck/SKILL.md +148 -148
- package/skills/claude-devfleet/SKILL.md +112 -112
- package/skills/click-path-audit/SKILL.md +245 -245
- package/skills/clickhouse-io/SKILL.md +445 -445
- package/skills/code-tour/SKILL.md +254 -254
- package/skills/codebase-onboarding/SKILL.md +234 -234
- package/skills/codehealth-mcp/SKILL.md +167 -167
- package/skills/coding-standards/SKILL.md +551 -551
- package/skills/competitive-platform-analysis/SKILL.md +214 -214
- package/skills/competitive-report-structure/SKILL.md +162 -162
- package/skills/compose-multiplatform-patterns/SKILL.md +300 -300
- package/skills/config-gc/SKILL.md +120 -120
- package/skills/configure-ecc/SKILL.md +385 -385
- package/skills/connections-optimizer/SKILL.md +190 -190
- package/skills/content-engine/SKILL.md +132 -132
- package/skills/content-hash-cache-pattern/SKILL.md +162 -162
- package/skills/context-budget/SKILL.md +136 -136
- package/skills/continuous-agent-loop/SKILL.md +46 -46
- package/skills/continuous-learning/SKILL.md +132 -132
- package/skills/continuous-learning-v2/SKILL.md +361 -361
- package/skills/contract-first/SKILL.md +287 -287
- package/skills/cost-aware-llm-pipeline/SKILL.md +184 -184
- package/skills/cost-tracking/SKILL.md +97 -97
- package/skills/council/SKILL.md +204 -204
- package/skills/cpp-coding-standards/SKILL.md +724 -724
- package/skills/cpp-testing/SKILL.md +325 -325
- package/skills/crosspost/SKILL.md +112 -112
- package/skills/csharp-testing/SKILL.md +322 -322
- package/skills/customer-billing-ops/SKILL.md +141 -141
- package/skills/customs-trade-compliance/SKILL.md +263 -263
- package/skills/dart-flutter-patterns/SKILL.md +564 -564
- package/skills/dashboard-builder/SKILL.md +109 -109
- package/skills/data-scraper-agent/SKILL.md +765 -765
- package/skills/data-throughput-accelerator/SKILL.md +74 -74
- package/skills/database-migrations/SKILL.md +430 -430
- package/skills/deep-research/SKILL.md +160 -160
- package/skills/defi-amm-security/SKILL.md +167 -167
- package/skills/delivery-gate/SKILL.md +126 -126
- package/skills/deployment-patterns/SKILL.md +428 -428
- package/skills/design-system/SKILL.md +83 -83
- package/skills/django-celery/SKILL.md +458 -458
- package/skills/django-patterns/SKILL.md +735 -735
- package/skills/django-security/SKILL.md +644 -644
- package/skills/django-tdd/SKILL.md +730 -730
- package/skills/django-verification/SKILL.md +470 -470
- package/skills/dmux-workflows/SKILL.md +192 -192
- package/skills/docker-patterns/SKILL.md +365 -365
- package/skills/documentation-lookup/SKILL.md +91 -91
- package/skills/dotnet-patterns/SKILL.md +322 -322
- package/skills/dynamic-workflow-mode/SKILL.md +124 -124
- package/skills/e2e-testing/SKILL.md +327 -327
- package/skills/ecc-guide/SKILL.md +190 -190
- package/skills/ecc-recipes/SKILL.md +149 -149
- package/skills/ecc-tools-cost-audit/SKILL.md +161 -161
- package/skills/email-ops/SKILL.md +122 -122
- package/skills/energy-procurement/SKILL.md +228 -228
- package/skills/enterprise-agent-ops/SKILL.md +51 -51
- package/skills/error-handling/SKILL.md +377 -377
- package/skills/eval-harness/SKILL.md +271 -271
- package/skills/evm-token-decimals/SKILL.md +131 -131
- package/skills/exa-search/SKILL.md +108 -108
- package/skills/fal-ai-media/SKILL.md +289 -289
- package/skills/fastapi-patterns/SKILL.md +514 -514
- package/skills/finance-billing-ops/SKILL.md +128 -128
- package/skills/flox-environments/SKILL.md +497 -497
- package/skills/flutter-dart-code-review/SKILL.md +436 -436
- package/skills/foundation-models-on-device/SKILL.md +243 -243
- package/skills/frontend-a11y/SKILL.md +446 -446
- package/skills/frontend-design-direction/SKILL.md +93 -93
- package/skills/frontend-patterns/SKILL.md +657 -657
- package/skills/frontend-slides/SKILL.md +185 -185
- package/skills/fsharp-testing/SKILL.md +281 -281
- package/skills/gan-style-harness/SKILL.md +279 -279
- package/skills/gateguard/SKILL.md +133 -133
- package/skills/generating-python-installer/SKILL.md +820 -820
- package/skills/git-workflow/SKILL.md +716 -716
- package/skills/github-ops/SKILL.md +145 -145
- package/skills/golang-patterns/SKILL.md +676 -676
- package/skills/golang-testing/SKILL.md +721 -721
- package/skills/google-workspace-ops/SKILL.md +96 -96
- package/skills/growth-log/SKILL.md +128 -128
- package/skills/healthcare-cdss-patterns/SKILL.md +246 -246
- package/skills/healthcare-emr-patterns/SKILL.md +160 -160
- package/skills/healthcare-eval-harness/SKILL.md +208 -208
- package/skills/healthcare-phi-compliance/SKILL.md +146 -146
- package/skills/hermes-imports/SKILL.md +89 -89
- package/skills/hexagonal-architecture/SKILL.md +277 -277
- package/skills/hipaa-compliance/SKILL.md +79 -79
- package/skills/homelab-network-readiness/SKILL.md +170 -170
- package/skills/homelab-network-setup/SKILL.md +130 -130
- package/skills/homelab-pihole-dns/SKILL.md +275 -275
- package/skills/homelab-vlan-segmentation/SKILL.md +312 -312
- package/skills/homelab-wireguard-vpn/SKILL.md +306 -306
- package/skills/hookify-rules/SKILL.md +128 -128
- package/skills/inherit-legacy-style/SKILL.md +157 -157
- package/skills/intent-driven-development/SKILL.md +360 -360
- package/skills/inventory-demand-planning/SKILL.md +247 -247
- package/skills/investor-materials/SKILL.md +97 -97
- package/skills/investor-outreach/SKILL.md +92 -92
- package/skills/ios-icon-gen/SKILL.md +158 -158
- package/skills/iterative-retrieval/SKILL.md +212 -212
- package/skills/ito-basket-compare/SKILL.md +64 -64
- package/skills/ito-compute/SKILL.md +129 -129
- package/skills/ito-data-atlas-agent/SKILL.md +64 -64
- package/skills/ito-market-intelligence/SKILL.md +61 -61
- package/skills/ito-trade-planner/SKILL.md +68 -68
- package/skills/java-coding-standards/SKILL.md +384 -384
- package/skills/jira-integration/SKILL.md +303 -303
- package/skills/jpa-patterns/SKILL.md +152 -152
- package/skills/knowledge-ops/SKILL.md +155 -155
- package/skills/kotlin-coroutines-flows/SKILL.md +285 -285
- package/skills/kotlin-exposed-patterns/SKILL.md +720 -720
- package/skills/kotlin-ktor-patterns/SKILL.md +690 -690
- package/skills/kotlin-patterns/SKILL.md +712 -712
- package/skills/kotlin-testing/SKILL.md +825 -825
- package/skills/kubernetes-patterns/SKILL.md +756 -756
- package/skills/laravel-patterns/SKILL.md +416 -416
- package/skills/laravel-plugin-discovery/SKILL.md +230 -230
- package/skills/laravel-security/SKILL.md +948 -948
- package/skills/laravel-tdd/SKILL.md +675 -675
- package/skills/laravel-verification/SKILL.md +180 -180
- package/skills/latency-critical-systems/SKILL.md +75 -75
- package/skills/lead-intelligence/SKILL.md +322 -322
- package/skills/liquid-glass-design/SKILL.md +279 -279
- package/skills/llm-trading-agent-security/SKILL.md +147 -147
- package/skills/logistics-exception-management/SKILL.md +222 -222
- package/skills/loop-design-check/SKILL.md +143 -143
- package/skills/mailtrap-email-integration/SKILL.md +77 -77
- package/skills/make-interfaces-feel-better/SKILL.md +152 -152
- package/skills/manim-video/SKILL.md +90 -90
- package/skills/market-research/SKILL.md +76 -76
- package/skills/marketing-campaign/SKILL.md +114 -114
- package/skills/mcp-server-patterns/SKILL.md +70 -70
- package/skills/messages-ops/SKILL.md +105 -105
- package/skills/ml-adoption-playbook/SKILL.md +57 -57
- package/skills/mle-workflow/SKILL.md +348 -348
- package/skills/motion-advanced/SKILL.md +596 -596
- package/skills/motion-foundations/SKILL.md +299 -299
- package/skills/motion-patterns/SKILL.md +434 -434
- package/skills/motion-ui/SKILL.md +576 -576
- package/skills/mysql-patterns/SKILL.md +413 -413
- package/skills/nanoclaw-repl/SKILL.md +34 -34
- package/skills/nestjs-patterns/SKILL.md +231 -231
- package/skills/netmiko-ssh-automation/SKILL.md +174 -174
- package/skills/network-bgp-diagnostics/SKILL.md +168 -168
- package/skills/network-config-validation/SKILL.md +211 -211
- package/skills/network-interface-health/SKILL.md +153 -153
- package/skills/nextjs-turbopack/SKILL.md +58 -58
- package/skills/nodejs-keccak256/SKILL.md +103 -103
- package/skills/nutrient-document-processing/SKILL.md +168 -168
- package/skills/nuxt4-patterns/SKILL.md +101 -101
- package/skills/openclaw-persona-forge/SKILL.md +289 -289
- package/skills/openclaw-persona-forge/references/avatar-style.md +124 -124
- package/skills/openclaw-persona-forge/references/boundary-rules.md +53 -53
- package/skills/openclaw-persona-forge/references/error-handling.md +53 -53
- package/skills/openclaw-persona-forge/references/identity-tension.md +48 -48
- package/skills/openclaw-persona-forge/references/naming-system.md +39 -39
- package/skills/openclaw-persona-forge/references/output-template.md +166 -166
- package/skills/opensource-pipeline/SKILL.md +256 -256
- package/skills/orch-add-feature/SKILL.md +45 -45
- package/skills/orch-build-mvp/SKILL.md +49 -49
- package/skills/orch-change-feature/SKILL.md +43 -43
- package/skills/orch-fix-defect/SKILL.md +43 -43
- package/skills/orch-pipeline/SKILL.md +121 -121
- package/skills/orch-refine-code/SKILL.md +44 -44
- package/skills/parallel-execution-optimizer/SKILL.md +74 -74
- package/skills/perl-patterns/SKILL.md +505 -505
- package/skills/perl-security/SKILL.md +504 -504
- package/skills/perl-testing/SKILL.md +476 -476
- package/skills/plan-canvas/SKILL.md +153 -153
- package/skills/plan-orchestrate/SKILL.md +263 -263
- package/skills/plankton-code-quality/SKILL.md +237 -237
- package/skills/postgres-patterns/SKILL.md +148 -148
- package/skills/prediction-market-oracle-research/SKILL.md +64 -64
- package/skills/prediction-market-risk-review/SKILL.md +61 -61
- package/skills/premium-web-design/SKILL.md +154 -154
- package/skills/prisma-patterns/SKILL.md +401 -401
- package/skills/product-capability/SKILL.md +142 -142
- package/skills/product-lens/SKILL.md +93 -93
- package/skills/production-audit/SKILL.md +207 -207
- package/skills/production-scheduling/SKILL.md +238 -238
- package/skills/project-flow-ops/SKILL.md +112 -112
- package/skills/prompt-optimizer/SKILL.md +398 -398
- package/skills/publish-website/SKILL.md +76 -0
- package/skills/python-patterns/SKILL.md +751 -751
- package/skills/python-testing/SKILL.md +817 -817
- package/skills/pytorch-patterns/SKILL.md +397 -397
- package/skills/quality-nonconformance/SKILL.md +260 -260
- package/skills/quarkus-patterns/SKILL.md +723 -723
- package/skills/quarkus-security/SKILL.md +468 -468
- package/skills/quarkus-tdd/SKILL.md +812 -812
- package/skills/quarkus-verification/SKILL.md +480 -480
- package/skills/ralphinho-rfc-pipeline/SKILL.md +68 -68
- package/skills/react-native-patterns/SKILL.md +326 -326
- package/skills/react-patterns/SKILL.md +342 -342
- package/skills/react-performance/SKILL.md +575 -575
- package/skills/react-testing/SKILL.md +424 -424
- package/skills/recsys-pipeline-architect/SKILL.md +115 -115
- package/skills/recursive-decision-ledger/SKILL.md +81 -81
- package/skills/redis-patterns/SKILL.md +404 -404
- package/skills/regex-vs-llm-structured-text/SKILL.md +221 -221
- package/skills/remotion-video-creation/SKILL.md +43 -43
- package/skills/repo-scan/SKILL.md +79 -79
- package/skills/research-ops/SKILL.md +113 -113
- package/skills/returns-reverse-logistics/SKILL.md +240 -240
- package/skills/rules-distill/SKILL.md +265 -265
- package/skills/rust-patterns/SKILL.md +500 -500
- package/skills/rust-testing/SKILL.md +501 -501
- package/skills/safety-guard/SKILL.md +76 -76
- package/skills/santa-method/SKILL.md +307 -307
- package/skills/scientific-db-pubmed-database/SKILL.md +176 -176
- package/skills/scientific-db-uspto-database/SKILL.md +178 -178
- package/skills/scientific-pkg-gget/SKILL.md +167 -167
- package/skills/scientific-thinking-literature-review/SKILL.md +193 -193
- package/skills/scientific-thinking-scholar-evaluation/SKILL.md +161 -161
- package/skills/search-first/SKILL.md +183 -183
- package/skills/security-bounty-hunter/SKILL.md +100 -100
- package/skills/security-review/SKILL.md +504 -504
- package/skills/security-scan/SKILL.md +166 -166
- package/skills/seo/SKILL.md +155 -155
- package/skills/skill-comply/SKILL.md +59 -59
- package/skills/skill-scout/SKILL.md +141 -141
- package/skills/skill-stocktake/SKILL.md +195 -195
- package/skills/social-graph-ranker/SKILL.md +155 -155
- package/skills/social-publisher/SKILL.md +130 -130
- package/skills/springboot-patterns/SKILL.md +315 -315
- package/skills/springboot-security/SKILL.md +273 -273
- package/skills/springboot-tdd/SKILL.md +159 -159
- package/skills/springboot-verification/SKILL.md +232 -232
- package/skills/strategic-compact/SKILL.md +142 -142
- package/skills/swift-actor-persistence/SKILL.md +144 -144
- package/skills/swift-concurrency-6-2/SKILL.md +216 -216
- package/skills/swift-protocol-di-testing/SKILL.md +191 -191
- package/skills/swiftui-patterns/SKILL.md +259 -259
- package/skills/taste/SKILL.md +264 -264
- package/skills/taste/references/genre-taxonomy.md +87 -87
- package/skills/tdd-workflow/SKILL.md +583 -583
- package/skills/team-agent-orchestration/SKILL.md +111 -111
- package/skills/team-builder/SKILL.md +169 -169
- package/skills/terminal-ops/SKILL.md +110 -110
- package/skills/tinystruct-patterns/SKILL.md +279 -279
- package/skills/tinystruct-patterns/references/architecture.md +90 -90
- package/skills/tinystruct-patterns/references/data-handling.md +60 -60
- package/skills/tinystruct-patterns/references/database.md +99 -99
- package/skills/tinystruct-patterns/references/routing.md +64 -64
- package/skills/tinystruct-patterns/references/system-usage.md +97 -97
- package/skills/tinystruct-patterns/references/testing.md +72 -72
- package/skills/token-budget-advisor/SKILL.md +134 -134
- package/skills/ui-demo/SKILL.md +466 -466
- package/skills/ui-to-vue/SKILL.md +135 -135
- package/skills/uncloud/SKILL.md +344 -344
- package/skills/unified-memory/SKILL.md +170 -170
- package/skills/unified-notifications-ops/SKILL.md +188 -188
- package/skills/verification-loop/SKILL.md +129 -129
- package/skills/video-editing/SKILL.md +311 -311
- package/skills/videodb/SKILL.md +375 -375
- package/skills/visa-doc-translate/SKILL.md +117 -117
- package/skills/vite-patterns/SKILL.md +450 -450
- package/skills/vue-patterns/SKILL.md +471 -471
- package/skills/windows-desktop-e2e/SKILL.md +888 -888
- package/skills/workspace-surface-audit/SKILL.md +126 -126
- package/skills/x-api/SKILL.md +235 -235
package/lib/snapshot.js
CHANGED
|
@@ -1,146 +1,146 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Undo for agent changes: a hidden git repository, kept outside the project,
|
|
3
|
-
* that records the project's files before a message runs so exactly what that
|
|
4
|
-
* message changed can be put back.
|
|
5
|
-
*
|
|
6
|
-
* The approach (a separate --git-dir pointed at the project as --work-tree,
|
|
7
|
-
* trees written with write-tree, files restored with checkout) follows
|
|
8
|
-
* opencode's snapshot/index.ts, MIT License, Copyright (c) 2025 opencode.
|
|
9
|
-
*
|
|
10
|
-
* It never touches the project's own .git: no commits, no index, no refs.
|
|
11
|
-
* The project's .gitignore still applies, plus a built-in list of heavy
|
|
12
|
-
* folders so a project without a .gitignore doesn't snapshot node_modules.
|
|
13
|
-
* Every function fails soft (returns null/false) when git is missing.
|
|
14
|
-
*/
|
|
15
|
-
const fs = require('fs');
|
|
16
|
-
const os = require('os');
|
|
17
|
-
const path = require('path');
|
|
18
|
-
const crypto = require('crypto');
|
|
19
|
-
const { execFile } = require('child_process');
|
|
20
|
-
|
|
21
|
-
const ROOT = path.join(os.homedir(), '.codeply', 'snapshots');
|
|
22
|
-
const TIMEOUT_MS = 30000;
|
|
23
|
-
const EXCLUDES = [
|
|
24
|
-
'node_modules/', '.git/', 'dist/', 'build/', 'out/', '.next/', '.nuxt/', '.turbo/', '.cache/', 'coverage/',
|
|
25
|
-
'__pycache__/', '.venv/', 'venv/', 'target/', '*.log', '.DS_Store', 'Thumbs.db',
|
|
26
|
-
];
|
|
27
|
-
const MAX_FILE_BYTES = 5 * 1024 * 1024;
|
|
28
|
-
|
|
29
|
-
function gitDirFor(cwd) {
|
|
30
|
-
const key = crypto.createHash('sha1').update(path.resolve(cwd).toLowerCase()).digest('hex').slice(0, 16);
|
|
31
|
-
return path.join(ROOT, key);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function git(cwd, args, { input } = {}) {
|
|
35
|
-
const gitDir = gitDirFor(cwd);
|
|
36
|
-
const full = [
|
|
37
|
-
'-c', 'core.autocrlf=false', '-c', 'core.longpaths=true', '-c', 'core.fsmonitor=false',
|
|
38
|
-
'-c', 'core.quotepath=false', '-c', `safe.directory=*`,
|
|
39
|
-
`--git-dir=${gitDir}`, `--work-tree=${path.resolve(cwd)}`, ...args,
|
|
40
|
-
];
|
|
41
|
-
return new Promise((resolve) => {
|
|
42
|
-
const child = execFile('git', full, { cwd, timeout: TIMEOUT_MS, windowsHide: true, maxBuffer: 32 * 1024 * 1024 },
|
|
43
|
-
(err, stdout, stderr) => resolve({ ok: !err, out: String(stdout || ''), err: String(stderr || (err && err.message) || '') }));
|
|
44
|
-
if (input != null) { child.stdin.end(input); }
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async function ensureRepo(cwd) {
|
|
49
|
-
const gitDir = gitDirFor(cwd);
|
|
50
|
-
if (!fs.existsSync(path.join(gitDir, 'HEAD'))) {
|
|
51
|
-
fs.mkdirSync(gitDir, { recursive: true });
|
|
52
|
-
const r = await git(cwd, ['init', '-q']);
|
|
53
|
-
if (!r.ok) return false;
|
|
54
|
-
fs.writeFileSync(path.join(gitDir, 'codeply-project.txt'), path.resolve(cwd));
|
|
55
|
-
}
|
|
56
|
-
// Refreshed every time so changes to the list reach existing repos.
|
|
57
|
-
fs.mkdirSync(path.join(gitDir, 'info'), { recursive: true });
|
|
58
|
-
fs.writeFileSync(path.join(gitDir, 'info', 'exclude'), EXCLUDES.join('\n') + '\n');
|
|
59
|
-
return true;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/** Big files (videos, datasets) that aren't gitignored are left out, like opencode does. */
|
|
63
|
-
async function excludeLargeUntracked(cwd) {
|
|
64
|
-
const r = await git(cwd, ['ls-files', '--others', '--exclude-standard', '-z']);
|
|
65
|
-
if (!r.ok || !r.out) return [];
|
|
66
|
-
const big = [];
|
|
67
|
-
for (const rel of r.out.split('\0').filter(Boolean)) {
|
|
68
|
-
try { if (fs.statSync(path.join(cwd, rel)).size > MAX_FILE_BYTES) big.push(rel); } catch {}
|
|
69
|
-
}
|
|
70
|
-
return big;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Record the project's current files.
|
|
75
|
-
* @returns {Promise<string|null>} a tree hash, or null if snapshots are unavailable
|
|
76
|
-
*/
|
|
77
|
-
async function track(cwd) {
|
|
78
|
-
try {
|
|
79
|
-
if (!cwd || !fs.existsSync(cwd) || !(await ensureRepo(cwd))) return null;
|
|
80
|
-
const big = await excludeLargeUntracked(cwd);
|
|
81
|
-
const add = await git(cwd, ['add', '-A', '--', '.', ...big.map((b) => `:(exclude,literal)${b}`)]);
|
|
82
|
-
if (!add.ok) return null;
|
|
83
|
-
const tree = await git(cwd, ['write-tree']);
|
|
84
|
-
return tree.ok ? tree.out.trim() || null : null;
|
|
85
|
-
} catch { return null; }
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/** Files that differ between two recorded trees. */
|
|
89
|
-
async function changedFiles(cwd, fromTree, toTree) {
|
|
90
|
-
if (!fromTree || !toTree || fromTree === toTree) return [];
|
|
91
|
-
const r = await git(cwd, ['diff-tree', '-r', '--name-status', '--no-renames', '-z', fromTree, toTree]);
|
|
92
|
-
if (!r.ok) return [];
|
|
93
|
-
const parts = r.out.split('\0').filter(Boolean);
|
|
94
|
-
const out = [];
|
|
95
|
-
for (let i = 0; i + 1 < parts.length; i += 2) {
|
|
96
|
-
out.push({ status: parts[i], file: parts[i + 1] }); // A added, M modified, D deleted
|
|
97
|
-
}
|
|
98
|
-
return out;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/** Does `file` exist in `tree`? */
|
|
102
|
-
async function inTree(cwd, tree, file) {
|
|
103
|
-
const r = await git(cwd, ['cat-file', '-e', `${tree}:${file}`]);
|
|
104
|
-
return r.ok;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Put `files` back the way they were in `tree`: restore the ones it had,
|
|
109
|
-
* delete the ones it didn't (they were created after it).
|
|
110
|
-
* @returns {Promise<{ok:boolean, restored:string[], removed:string[], failed:string[]}>}
|
|
111
|
-
*/
|
|
112
|
-
async function restore(cwd, tree, files) {
|
|
113
|
-
const restored = [], removed = [], failed = [];
|
|
114
|
-
if (!tree || !(await ensureRepo(cwd))) return { ok: false, restored, removed, failed: files.slice() };
|
|
115
|
-
const existed = [];
|
|
116
|
-
for (const f of files) {
|
|
117
|
-
if (await inTree(cwd, tree, f)) existed.push(f);
|
|
118
|
-
else {
|
|
119
|
-
try { fs.rmSync(path.join(cwd, f), { force: true }); removed.push(f); } catch { failed.push(f); }
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
for (let i = 0; i < existed.length; i += 100) {
|
|
123
|
-
const batch = existed.slice(i, i + 100);
|
|
124
|
-
const r = await git(cwd, ['checkout', tree, '--', ...batch]);
|
|
125
|
-
if (r.ok) restored.push(...batch);
|
|
126
|
-
else {
|
|
127
|
-
// One bad path shouldn't block the rest.
|
|
128
|
-
for (const f of batch) {
|
|
129
|
-
const one = await git(cwd, ['checkout', tree, '--', f]);
|
|
130
|
-
(one.ok ? restored : failed).push(f);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
return { ok: failed.length === 0, restored, removed, failed };
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/** Is git usable here at all? Cached. */
|
|
138
|
-
let gitAvailable;
|
|
139
|
-
function available() {
|
|
140
|
-
if (gitAvailable !== undefined) return Promise.resolve(gitAvailable);
|
|
141
|
-
return new Promise((resolve) => {
|
|
142
|
-
execFile('git', ['--version'], { timeout: 5000, windowsHide: true }, (err) => { gitAvailable = !err; resolve(gitAvailable); });
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
module.exports = { track, changedFiles, restore, available, gitDirFor };
|
|
1
|
+
/**
|
|
2
|
+
* Undo for agent changes: a hidden git repository, kept outside the project,
|
|
3
|
+
* that records the project's files before a message runs so exactly what that
|
|
4
|
+
* message changed can be put back.
|
|
5
|
+
*
|
|
6
|
+
* The approach (a separate --git-dir pointed at the project as --work-tree,
|
|
7
|
+
* trees written with write-tree, files restored with checkout) follows
|
|
8
|
+
* opencode's snapshot/index.ts, MIT License, Copyright (c) 2025 opencode.
|
|
9
|
+
*
|
|
10
|
+
* It never touches the project's own .git: no commits, no index, no refs.
|
|
11
|
+
* The project's .gitignore still applies, plus a built-in list of heavy
|
|
12
|
+
* folders so a project without a .gitignore doesn't snapshot node_modules.
|
|
13
|
+
* Every function fails soft (returns null/false) when git is missing.
|
|
14
|
+
*/
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const os = require('os');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
const crypto = require('crypto');
|
|
19
|
+
const { execFile } = require('child_process');
|
|
20
|
+
|
|
21
|
+
const ROOT = path.join(os.homedir(), '.codeply', 'snapshots');
|
|
22
|
+
const TIMEOUT_MS = 30000;
|
|
23
|
+
const EXCLUDES = [
|
|
24
|
+
'node_modules/', '.git/', 'dist/', 'build/', 'out/', '.next/', '.nuxt/', '.turbo/', '.cache/', 'coverage/',
|
|
25
|
+
'__pycache__/', '.venv/', 'venv/', 'target/', '*.log', '.DS_Store', 'Thumbs.db',
|
|
26
|
+
];
|
|
27
|
+
const MAX_FILE_BYTES = 5 * 1024 * 1024;
|
|
28
|
+
|
|
29
|
+
function gitDirFor(cwd) {
|
|
30
|
+
const key = crypto.createHash('sha1').update(path.resolve(cwd).toLowerCase()).digest('hex').slice(0, 16);
|
|
31
|
+
return path.join(ROOT, key);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function git(cwd, args, { input } = {}) {
|
|
35
|
+
const gitDir = gitDirFor(cwd);
|
|
36
|
+
const full = [
|
|
37
|
+
'-c', 'core.autocrlf=false', '-c', 'core.longpaths=true', '-c', 'core.fsmonitor=false',
|
|
38
|
+
'-c', 'core.quotepath=false', '-c', `safe.directory=*`,
|
|
39
|
+
`--git-dir=${gitDir}`, `--work-tree=${path.resolve(cwd)}`, ...args,
|
|
40
|
+
];
|
|
41
|
+
return new Promise((resolve) => {
|
|
42
|
+
const child = execFile('git', full, { cwd, timeout: TIMEOUT_MS, windowsHide: true, maxBuffer: 32 * 1024 * 1024 },
|
|
43
|
+
(err, stdout, stderr) => resolve({ ok: !err, out: String(stdout || ''), err: String(stderr || (err && err.message) || '') }));
|
|
44
|
+
if (input != null) { child.stdin.end(input); }
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function ensureRepo(cwd) {
|
|
49
|
+
const gitDir = gitDirFor(cwd);
|
|
50
|
+
if (!fs.existsSync(path.join(gitDir, 'HEAD'))) {
|
|
51
|
+
fs.mkdirSync(gitDir, { recursive: true });
|
|
52
|
+
const r = await git(cwd, ['init', '-q']);
|
|
53
|
+
if (!r.ok) return false;
|
|
54
|
+
fs.writeFileSync(path.join(gitDir, 'codeply-project.txt'), path.resolve(cwd));
|
|
55
|
+
}
|
|
56
|
+
// Refreshed every time so changes to the list reach existing repos.
|
|
57
|
+
fs.mkdirSync(path.join(gitDir, 'info'), { recursive: true });
|
|
58
|
+
fs.writeFileSync(path.join(gitDir, 'info', 'exclude'), EXCLUDES.join('\n') + '\n');
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Big files (videos, datasets) that aren't gitignored are left out, like opencode does. */
|
|
63
|
+
async function excludeLargeUntracked(cwd) {
|
|
64
|
+
const r = await git(cwd, ['ls-files', '--others', '--exclude-standard', '-z']);
|
|
65
|
+
if (!r.ok || !r.out) return [];
|
|
66
|
+
const big = [];
|
|
67
|
+
for (const rel of r.out.split('\0').filter(Boolean)) {
|
|
68
|
+
try { if (fs.statSync(path.join(cwd, rel)).size > MAX_FILE_BYTES) big.push(rel); } catch {}
|
|
69
|
+
}
|
|
70
|
+
return big;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Record the project's current files.
|
|
75
|
+
* @returns {Promise<string|null>} a tree hash, or null if snapshots are unavailable
|
|
76
|
+
*/
|
|
77
|
+
async function track(cwd) {
|
|
78
|
+
try {
|
|
79
|
+
if (!cwd || !fs.existsSync(cwd) || !(await ensureRepo(cwd))) return null;
|
|
80
|
+
const big = await excludeLargeUntracked(cwd);
|
|
81
|
+
const add = await git(cwd, ['add', '-A', '--', '.', ...big.map((b) => `:(exclude,literal)${b}`)]);
|
|
82
|
+
if (!add.ok) return null;
|
|
83
|
+
const tree = await git(cwd, ['write-tree']);
|
|
84
|
+
return tree.ok ? tree.out.trim() || null : null;
|
|
85
|
+
} catch { return null; }
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Files that differ between two recorded trees. */
|
|
89
|
+
async function changedFiles(cwd, fromTree, toTree) {
|
|
90
|
+
if (!fromTree || !toTree || fromTree === toTree) return [];
|
|
91
|
+
const r = await git(cwd, ['diff-tree', '-r', '--name-status', '--no-renames', '-z', fromTree, toTree]);
|
|
92
|
+
if (!r.ok) return [];
|
|
93
|
+
const parts = r.out.split('\0').filter(Boolean);
|
|
94
|
+
const out = [];
|
|
95
|
+
for (let i = 0; i + 1 < parts.length; i += 2) {
|
|
96
|
+
out.push({ status: parts[i], file: parts[i + 1] }); // A added, M modified, D deleted
|
|
97
|
+
}
|
|
98
|
+
return out;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Does `file` exist in `tree`? */
|
|
102
|
+
async function inTree(cwd, tree, file) {
|
|
103
|
+
const r = await git(cwd, ['cat-file', '-e', `${tree}:${file}`]);
|
|
104
|
+
return r.ok;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Put `files` back the way they were in `tree`: restore the ones it had,
|
|
109
|
+
* delete the ones it didn't (they were created after it).
|
|
110
|
+
* @returns {Promise<{ok:boolean, restored:string[], removed:string[], failed:string[]}>}
|
|
111
|
+
*/
|
|
112
|
+
async function restore(cwd, tree, files) {
|
|
113
|
+
const restored = [], removed = [], failed = [];
|
|
114
|
+
if (!tree || !(await ensureRepo(cwd))) return { ok: false, restored, removed, failed: files.slice() };
|
|
115
|
+
const existed = [];
|
|
116
|
+
for (const f of files) {
|
|
117
|
+
if (await inTree(cwd, tree, f)) existed.push(f);
|
|
118
|
+
else {
|
|
119
|
+
try { fs.rmSync(path.join(cwd, f), { force: true }); removed.push(f); } catch { failed.push(f); }
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
for (let i = 0; i < existed.length; i += 100) {
|
|
123
|
+
const batch = existed.slice(i, i + 100);
|
|
124
|
+
const r = await git(cwd, ['checkout', tree, '--', ...batch]);
|
|
125
|
+
if (r.ok) restored.push(...batch);
|
|
126
|
+
else {
|
|
127
|
+
// One bad path shouldn't block the rest.
|
|
128
|
+
for (const f of batch) {
|
|
129
|
+
const one = await git(cwd, ['checkout', tree, '--', f]);
|
|
130
|
+
(one.ok ? restored : failed).push(f);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return { ok: failed.length === 0, restored, removed, failed };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Is git usable here at all? Cached. */
|
|
138
|
+
let gitAvailable;
|
|
139
|
+
function available() {
|
|
140
|
+
if (gitAvailable !== undefined) return Promise.resolve(gitAvailable);
|
|
141
|
+
return new Promise((resolve) => {
|
|
142
|
+
execFile('git', ['--version'], { timeout: 5000, windowsHide: true }, (err) => { gitAvailable = !err; resolve(gitAvailable); });
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
module.exports = { track, changedFiles, restore, available, gitDirFor };
|
package/lib/telemetry.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Usage counts for Codeply's admin dashboard: which app was used (Craft, Crew
|
|
3
|
+
* or the CLI), which model, Auto or the user's own key, tokens in and out, and
|
|
4
|
+
* the app version. Never prompts, replies, files or anything typed.
|
|
5
|
+
*
|
|
6
|
+
* Events wait in memory and go out in small batches (about once a minute, and
|
|
7
|
+
* once more before the process exits) to Codeply's track function, only when
|
|
8
|
+
* the user is signed in to Codeply. A Cloud run inside GitHub Actions has no
|
|
9
|
+
* sign-in; it sends with the usage key Craft put in the repo's secrets
|
|
10
|
+
* (CODEPLY_TRACK_KEY). Off with CODEPLY_TELEMETRY=0, or "telemetry": false in
|
|
11
|
+
* ~/.codeply/config.json.
|
|
12
|
+
*
|
|
13
|
+
* Which app a call belongs to: the host sets a default (Craft's main process
|
|
14
|
+
* says "craft"; the CLI is "cli" by default), and Crew runs its bots inside
|
|
15
|
+
* withProduct('crew', ...), which follows the call through every await.
|
|
16
|
+
*/
|
|
17
|
+
const { AsyncLocalStorage } = require('async_hooks');
|
|
18
|
+
|
|
19
|
+
const als = new AsyncLocalStorage();
|
|
20
|
+
const defaults = { product: 'cli', version: '', platform: process.platform };
|
|
21
|
+
try { defaults.version = require('../package.json').version || ''; } catch {}
|
|
22
|
+
let queue = [];
|
|
23
|
+
let timer = null;
|
|
24
|
+
let sending = false;
|
|
25
|
+
const FLUSH_MS = 60_000;
|
|
26
|
+
const MAX_QUEUE = 500;
|
|
27
|
+
|
|
28
|
+
function enabled() {
|
|
29
|
+
if (process.env.CODEPLY_TELEMETRY === '0') return false;
|
|
30
|
+
try { return require('./config.js').getConfig().telemetry !== false; } catch { return true; }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** The host says what it is: setDefaults({ product: 'craft', version: '1.1.14' }). */
|
|
34
|
+
function setDefaults(d) { Object.assign(defaults, d || {}); }
|
|
35
|
+
|
|
36
|
+
/** Run fn with every AI call inside it counted for this product. */
|
|
37
|
+
function withProduct(product, fn) { return als.run({ product }, fn); }
|
|
38
|
+
|
|
39
|
+
function currentProduct() { const s = als.getStore(); return (s && s.product) || defaults.product; }
|
|
40
|
+
|
|
41
|
+
function push(ev) {
|
|
42
|
+
if (!enabled()) return;
|
|
43
|
+
queue.push({ product: currentProduct(), version: defaults.version, platform: defaults.platform, at: new Date().toISOString(), ...ev });
|
|
44
|
+
if (queue.length > MAX_QUEUE) queue = queue.slice(-MAX_QUEUE);
|
|
45
|
+
if (queue.length >= 40) flush();
|
|
46
|
+
else if (!timer) { timer = setTimeout(() => { timer = null; flush(); }, FLUSH_MS); if (timer.unref) timer.unref(); }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** One model call: { model, provider, tokens_in, tokens_out, ms }. */
|
|
50
|
+
function recordAi(ev) { push({ kind: 'ai', ...ev }); }
|
|
51
|
+
|
|
52
|
+
/** The app was opened (counts active users even on a day with no AI call), once an hour at most. */
|
|
53
|
+
const lastOpen = new Map();
|
|
54
|
+
function recordOpen(product) {
|
|
55
|
+
const p = product || currentProduct();
|
|
56
|
+
if (Date.now() - (lastOpen.get(p) || 0) < 3600_000) return;
|
|
57
|
+
lastOpen.set(p, Date.now());
|
|
58
|
+
push({ kind: 'open', product: p });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function flush() {
|
|
62
|
+
if (sending || !queue.length || !enabled()) return;
|
|
63
|
+
sending = true;
|
|
64
|
+
const batch = queue.slice(0, 100);
|
|
65
|
+
try {
|
|
66
|
+
const auth = require('./auth.js');
|
|
67
|
+
const trackKey = String(process.env.CODEPLY_TRACK_KEY || '').trim();
|
|
68
|
+
const token = trackKey ? null : await auth.getAccessToken().catch(() => null);
|
|
69
|
+
if (!token && !trackKey) { queue = []; return; } // signed out: nothing is kept or sent
|
|
70
|
+
const res = await fetch(`${auth.SUPABASE_URL}/functions/v1/track`, {
|
|
71
|
+
method: 'POST',
|
|
72
|
+
headers: { ...(trackKey ? { 'x-codeply-track-key': trackKey } : { Authorization: `Bearer ${token}` }), apikey: auth.SUPABASE_ANON_KEY, 'Content-Type': 'application/json' },
|
|
73
|
+
body: JSON.stringify({ events: batch }),
|
|
74
|
+
signal: AbortSignal.timeout(15_000),
|
|
75
|
+
});
|
|
76
|
+
if (res.ok || res.status === 400) queue = queue.slice(batch.length); // sent (or unusable): drop them
|
|
77
|
+
} catch {
|
|
78
|
+
// Offline: they wait for the next flush.
|
|
79
|
+
} finally {
|
|
80
|
+
sending = false;
|
|
81
|
+
if (queue.length && !timer) { timer = setTimeout(() => { timer = null; flush(); }, FLUSH_MS); if (timer.unref) timer.unref(); }
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Token counts from any provider's response shape, or an estimate from the text. */
|
|
86
|
+
function tokensOf(data, messages) {
|
|
87
|
+
const u = (data && data.usage) || {};
|
|
88
|
+
const tin = u.prompt_tokens ?? u.input_tokens ?? (data && data.prompt_eval_count);
|
|
89
|
+
const tout = u.completion_tokens ?? u.output_tokens ?? (data && data.eval_count);
|
|
90
|
+
if (tin != null || tout != null) return { tokens_in: Number(tin) || 0, tokens_out: Number(tout) || 0 };
|
|
91
|
+
const chars = (arr) => arr.reduce((n, m) => n + (typeof m.content === 'string' ? m.content.length : JSON.stringify(m.content || '').length), 0);
|
|
92
|
+
const reply = data && data.choices && data.choices[0] && data.choices[0].message ? String(data.choices[0].message.content || '') : '';
|
|
93
|
+
return { tokens_in: Math.round(chars(messages || []) / 4), tokens_out: Math.round(reply.length / 4) };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Send what is waiting before the process ends (short CLI commands and Cloud
|
|
97
|
+
// runs finish long before the minute timer). beforeExit fires again after the
|
|
98
|
+
// send; with the queue empty, flush does nothing and the process exits.
|
|
99
|
+
let exitFlushes = 0;
|
|
100
|
+
process.on('beforeExit', () => { if (queue.length && exitFlushes++ < 3) flush(); });
|
|
101
|
+
|
|
102
|
+
module.exports = { setDefaults, withProduct, currentProduct, recordAi, recordOpen, flush, tokensOf };
|