@kaikybrofc/omnizap-system 2.2.5 → 2.2.6

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/.prettierrc ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "printWidth": 12000,
3
+ "tabWidth": 2,
4
+ "useTabs": false,
5
+ "semi": true,
6
+ "singleQuote": true,
7
+ "trailingComma": "all",
8
+ "bracketSpacing": true,
9
+ "arrowParens": "always",
10
+ "endOfLine": "lf",
11
+ "bracketSameLine": false,
12
+ "proseWrap": "preserve",
13
+ "htmlWhitespaceSensitivity": "css",
14
+ "quoteProps": "as-needed",
15
+ "embeddedLanguageFormatting": "auto"
16
+ }
package/README.md CHANGED
@@ -53,25 +53,25 @@ Atualização em cache: **30 minutos** por padrão (`README_SUMMARY_CACHE_SECOND
53
53
  <!-- README_SNAPSHOT:START -->
54
54
  ### Snapshot do Sistema
55
55
 
56
- > Atualizado em `2026-02-28T04:28:50.529Z` | cache `1800s`
56
+ > Atualizado em `2026-02-28T06:26:02.459Z` | cache `1800s`
57
57
 
58
58
  | Métrica | Valor |
59
59
  | --- | ---: |
60
60
  | Usuários (lid_map) | 5.504 |
61
61
  | Grupos | 116 |
62
- | Packs | 294 |
63
- | Stickers | 6.796 |
64
- | Mensagens registradas | 440.582 |
62
+ | Packs | 293 |
63
+ | Stickers | 6.775 |
64
+ | Mensagens registradas | 440.765 |
65
65
 
66
66
  #### Tipos de mensagem mais usados (amostra: 25.000)
67
67
  | Tipo | Total |
68
68
  | --- | ---: |
69
- | `texto` | 16.293 |
70
- | `figurinha` | 4.718 |
71
- | `reacao` | 1.506 |
72
- | `imagem` | 1.286 |
73
- | `outros` | 744 |
74
- | `video` | 232 |
69
+ | `texto` | 16.279 |
70
+ | `figurinha` | 4.721 |
71
+ | `reacao` | 1.504 |
72
+ | `imagem` | 1.290 |
73
+ | `outros` | 757 |
74
+ | `video` | 228 |
75
75
  | `audio` | 216 |
76
76
  | `documento` | 5 |
77
77
 
@@ -8,6 +8,8 @@ const DEFAULT_AUTO_PACK_NAME = process.env.STICKER_PACK_AUTO_PACK_NAME || 'pack'
8
8
  const AUTO_PACK_TARGET_VISIBILITY = 'unlisted';
9
9
  const AUTO_COLLECT_ENABLED = process.env.STICKER_PACK_AUTO_COLLECT_ENABLED !== 'false';
10
10
  const AUTO_PACK_NAME_MAX_LENGTH = 120;
11
+ const AUTO_PACK_DESCRIPTION_MARKER = '[auto-pack:collector]';
12
+ const AUTO_PACK_DESCRIPTION_TEXT = 'Coleção automática de figurinhas criadas pelo usuário.';
11
13
  const normalizeVisibility = (value) => String(value || '').trim().toLowerCase();
12
14
 
13
15
  /**
@@ -43,6 +45,42 @@ const normalizeAutoPackName = (value, { fallback = 'pack', maxLength = AUTO_PACK
43
45
  return normalized || fallback;
44
46
  };
45
47
 
48
+ const buildAutoPackDescription = () => `${AUTO_PACK_DESCRIPTION_TEXT} ${AUTO_PACK_DESCRIPTION_MARKER}`.trim();
49
+
50
+ const isThemeCurationAutoPack = (pack) => {
51
+ if (!pack || typeof pack !== 'object') return false;
52
+ const description = String(pack.description || '').toLowerCase();
53
+ if (description.includes('[auto-theme:') || description.includes('[auto-tag:')) return true;
54
+ if (String(pack.name || '').trim().toLowerCase().startsWith('[auto]')) return true;
55
+ return Boolean(String(pack.pack_theme_key || '').trim());
56
+ };
57
+
58
+ const isAutoCollectorPack = (pack) => {
59
+ if (!pack || typeof pack !== 'object') return false;
60
+ if (isThemeCurationAutoPack(pack)) return false;
61
+
62
+ const description = String(pack.description || '').toLowerCase();
63
+ if (description.includes(AUTO_PACK_DESCRIPTION_MARKER)) return true;
64
+ if (description.includes('coleção automática de figurinhas criadas pelo usuário.')) return true;
65
+
66
+ const normalizedName = normalizeAutoPackName(pack.name, { fallback: '', maxLength: AUTO_PACK_NAME_MAX_LENGTH });
67
+ if (!normalizedName) return false;
68
+
69
+ const base = normalizeAutoPackName(DEFAULT_AUTO_PACK_NAME, { fallback: 'pack', maxLength: AUTO_PACK_NAME_MAX_LENGTH });
70
+ const matcher = new RegExp(`^${escapeRegex(base.toLowerCase())}\\d+$`, 'i');
71
+ const looksLikeLegacyCollector = matcher.test(normalizedName);
72
+ if (looksLikeLegacyCollector) return true;
73
+
74
+ return pack.is_auto_pack === true || Number(pack.is_auto_pack || 0) === 1;
75
+ };
76
+
77
+ const isUserManagedPackCandidate = (pack) => {
78
+ if (!pack || typeof pack !== 'object') return false;
79
+ if (isThemeCurationAutoPack(pack)) return false;
80
+ if (isAutoCollectorPack(pack)) return false;
81
+ return Number(pack.is_auto_pack || 0) !== 1;
82
+ };
83
+
46
84
  /**
47
85
  * Monta candidato incremental para nome de pack automático.
48
86
  *
@@ -177,21 +215,37 @@ export function createAutoPackCollector(options = {}) {
177
215
  const ensureTargetPack = async ({ ownerJid, senderName }) => {
178
216
  const packs = await deps.stickerPackService.listPacks({ ownerJid, limit: 30 });
179
217
  if (packs.length > 0) {
218
+ // Prioriza pack gerenciado pelo usuário (pack atual/manual).
219
+ const userManagedPacks = packs.filter((entry) => isUserManagedPackCandidate(entry));
220
+ if (userManagedPacks.length > 0) {
221
+ return {
222
+ pack: userManagedPacks[0],
223
+ packs,
224
+ };
225
+ }
226
+
227
+ const managedAutoPacks = packs.filter((entry) => isAutoCollectorPack(entry));
180
228
  const preferredPack =
181
- packs.find((entry) => normalizeVisibility(entry?.visibility) === AUTO_PACK_TARGET_VISIBILITY) || packs[0];
182
- const ensuredPack = await ensureAutoPackVisibility({ ownerJid, pack: preferredPack });
183
- return {
184
- pack: ensuredPack,
185
- packs,
186
- };
229
+ managedAutoPacks.find((entry) => normalizeVisibility(entry?.visibility) === AUTO_PACK_TARGET_VISIBILITY)
230
+ || managedAutoPacks[0]
231
+ || null;
232
+
233
+ if (preferredPack) {
234
+ const ensuredPack = await ensureAutoPackVisibility({ ownerJid, pack: preferredPack });
235
+ return {
236
+ pack: ensuredPack,
237
+ packs,
238
+ };
239
+ }
187
240
  }
188
241
 
189
242
  const created = await deps.stickerPackService.createPack({
190
243
  ownerJid,
191
244
  name: makeAutoPackName([]),
192
245
  publisher: sanitizeText(senderName, 120, { allowEmpty: true }) || 'OmniZap',
193
- description: 'Coleção automática de figurinhas criadas pelo usuário.',
246
+ description: buildAutoPackDescription(),
194
247
  visibility: AUTO_PACK_TARGET_VISIBILITY,
248
+ isAutoPack: true,
195
249
  });
196
250
 
197
251
  return {
@@ -250,8 +304,9 @@ export function createAutoPackCollector(options = {}) {
250
304
  ownerJid,
251
305
  name: makeAutoPackName(packs),
252
306
  publisher: sanitizeText(senderName, 120, { allowEmpty: true }) || targetPack.publisher || 'OmniZap',
253
- description: 'Coleção automática de figurinhas criadas pelo usuário.',
307
+ description: buildAutoPackDescription(),
254
308
  visibility: AUTO_PACK_TARGET_VISIBILITY,
309
+ isAutoPack: true,
255
310
  });
256
311
 
257
312
  const updated = await deps.stickerPackService.addStickerToPack({
@@ -157,6 +157,7 @@ const STICKER_LOGIN_WEB_PATH = normalizeBasePath(process.env.STICKER_LOGIN_WEB_P
157
157
  const USER_PROFILE_WEB_PATH = normalizeBasePath(process.env.USER_PROFILE_WEB_PATH, '/user');
158
158
  const STICKER_DATA_PUBLIC_PATH = normalizeBasePath(process.env.STICKER_DATA_PUBLIC_PATH, '/data');
159
159
  const STICKER_DATA_PUBLIC_DIR = path.resolve(process.env.STICKER_DATA_PUBLIC_DIR || path.join(process.cwd(), 'data'));
160
+ const STICKER_WEB_ASSET_VERSION = sanitizeText(process.env.STICKER_WEB_ASSET_VERSION || '', 64, { allowEmpty: true }) || '';
160
161
  const CATALOG_PUBLIC_DIR = path.resolve(process.cwd(), 'public');
161
162
  const CATALOG_TEMPLATE_PATH = path.join(CATALOG_PUBLIC_DIR, 'stickers', 'index.html');
162
163
  const CREATE_PACK_TEMPLATE_PATH = path.join(CATALOG_PUBLIC_DIR, 'stickers', 'create', 'index.html');
@@ -171,7 +172,19 @@ const MAX_ORPHAN_LIST_LIMIT = clampInt(process.env.STICKER_ORPHAN_LIST_MAX_LIMIT
171
172
  const DEFAULT_DATA_LIST_LIMIT = clampInt(process.env.STICKER_DATA_LIST_LIMIT, 50, 1, 200);
172
173
  const MAX_DATA_LIST_LIMIT = clampInt(process.env.STICKER_DATA_LIST_MAX_LIMIT, 200, 1, 500);
173
174
  const MAX_DATA_SCAN_FILES = clampInt(process.env.STICKER_DATA_SCAN_MAX_FILES, 10000, 100, 50000);
174
- const ASSET_CACHE_SECONDS = clampInt(process.env.STICKER_WEB_ASSET_CACHE_SECONDS, 60 * 10, 0, 60 * 60 * 24 * 7);
175
+ const ASSET_CACHE_SECONDS = clampInt(
176
+ process.env.STICKER_WEB_ASSET_CACHE_SECONDS,
177
+ 60 * 60 * 24 * 30,
178
+ 60 * 60,
179
+ 60 * 60 * 24 * 365,
180
+ );
181
+ const STATIC_TEXT_CACHE_SECONDS = clampInt(process.env.STICKER_WEB_STATIC_TEXT_CACHE_SECONDS, 60 * 60, 60, 60 * 60 * 24 * 30);
182
+ const IMMUTABLE_ASSET_CACHE_SECONDS = clampInt(
183
+ process.env.STICKER_WEB_IMMUTABLE_ASSET_CACHE_SECONDS,
184
+ 60 * 60 * 24 * 365,
185
+ 60 * 60,
186
+ 60 * 60 * 24 * 365,
187
+ );
175
188
  const STICKER_WEB_WHATSAPP_MESSAGE_TEMPLATE =
176
189
  String(process.env.STICKER_WEB_WHATSAPP_MESSAGE_TEMPLATE || '/pack send {{pack_key}}').trim() ||
177
190
  '/pack send {{pack_key}}';
@@ -1972,10 +1985,15 @@ const clearGoogleWebSessionCookie = (req, res) => {
1972
1985
  };
1973
1986
 
1974
1987
  const sendAsset = (req, res, buffer, mimetype = 'image/webp') => {
1988
+ const maxAgeSeconds = Math.max(60 * 60 * 24, ASSET_CACHE_SECONDS);
1989
+ const staleWhileRevalidateSeconds = Math.min(60 * 60 * 24 * 7, Math.max(300, maxAgeSeconds));
1975
1990
  res.statusCode = 200;
1976
1991
  res.setHeader('Content-Type', mimetype);
1977
1992
  res.setHeader('Content-Length', String(buffer.length));
1978
- res.setHeader('Cache-Control', `public, max-age=${ASSET_CACHE_SECONDS}`);
1993
+ res.setHeader(
1994
+ 'Cache-Control',
1995
+ `public, max-age=${maxAgeSeconds}, stale-while-revalidate=${staleWhileRevalidateSeconds}`,
1996
+ );
1979
1997
  if (req.method === 'HEAD') {
1980
1998
  res.end();
1981
1999
  return;
@@ -2214,8 +2232,12 @@ const buildStickerAssetUrl = (packKey, stickerId) =>
2214
2232
  `${STICKER_API_BASE_PATH}/${encodeURIComponent(packKey)}/stickers/${encodeURIComponent(stickerId)}.webp`;
2215
2233
  const buildOrphanStickersApiUrl = () => STICKER_ORPHAN_API_PATH;
2216
2234
  const buildDataAssetApiBaseUrl = () => `${STICKER_API_BASE_PATH}/data-files`;
2217
- const buildCatalogStylesUrl = () => `${STICKER_WEB_PATH}/assets/styles.css`;
2218
- const buildCatalogScriptUrl = () => `${STICKER_WEB_PATH}/assets/catalog.js`;
2235
+ const CATALOG_STYLES_WEB_PATH = `${STICKER_WEB_PATH}/assets/styles.css`;
2236
+ const CATALOG_SCRIPT_WEB_PATH = `${STICKER_WEB_PATH}/assets/catalog.js`;
2237
+ const appendAssetVersionQuery = (assetPath) =>
2238
+ STICKER_WEB_ASSET_VERSION ? `${assetPath}?v=${encodeURIComponent(STICKER_WEB_ASSET_VERSION)}` : assetPath;
2239
+ const buildCatalogStylesUrl = () => appendAssetVersionQuery(CATALOG_STYLES_WEB_PATH);
2240
+ const buildCatalogScriptUrl = () => appendAssetVersionQuery(CATALOG_SCRIPT_WEB_PATH);
2219
2241
  const buildDataAssetUrl = (relativePath) =>
2220
2242
  `${STICKER_DATA_PUBLIC_PATH}/${String(relativePath)
2221
2243
  .split('/')
@@ -3467,9 +3489,13 @@ const handleSitemapRequest = async (req, res) => {
3467
3489
  const sendStaticTextFile = async (req, res, filePath, contentType) => {
3468
3490
  try {
3469
3491
  const body = await fs.readFile(filePath, 'utf8');
3492
+ const hasVersionQuery = /(?:\?|&)v=/.test(String(req.url || ''));
3493
+ const cacheControl = hasVersionQuery
3494
+ ? `public, max-age=${IMMUTABLE_ASSET_CACHE_SECONDS}, immutable`
3495
+ : `public, max-age=${STATIC_TEXT_CACHE_SECONDS}, stale-while-revalidate=${Math.min(86400, STATIC_TEXT_CACHE_SECONDS * 4)}`;
3470
3496
  res.statusCode = 200;
3471
3497
  res.setHeader('Content-Type', contentType);
3472
- res.setHeader('Cache-Control', 'public, max-age=300');
3498
+ res.setHeader('Cache-Control', cacheControl);
3473
3499
  if (req.method === 'HEAD') {
3474
3500
  res.end();
3475
3501
  return true;
@@ -3493,11 +3519,11 @@ const sendStaticTextFile = async (req, res, filePath, contentType) => {
3493
3519
  };
3494
3520
 
3495
3521
  const handleCatalogStaticAssetRequest = async (req, res, pathname) => {
3496
- if (pathname === buildCatalogStylesUrl()) {
3522
+ if (pathname === CATALOG_STYLES_WEB_PATH) {
3497
3523
  return sendStaticTextFile(req, res, CATALOG_STYLES_FILE_PATH, 'text/css; charset=utf-8');
3498
3524
  }
3499
3525
 
3500
- if (pathname === buildCatalogScriptUrl()) {
3526
+ if (pathname === CATALOG_SCRIPT_WEB_PATH) {
3501
3527
  return sendStaticTextFile(req, res, CATALOG_SCRIPT_FILE_PATH, 'application/javascript; charset=utf-8');
3502
3528
  }
3503
3529
 
@@ -0,0 +1,83 @@
1
+ # Sticker Catalog 10x Baseline e SLOs
2
+
3
+ Este documento define a baseline operacional da camada HTTP + pipeline de classificação para o módulo de stickers.
4
+
5
+ ## 1. Metas SLO (fase inicial)
6
+
7
+ ### HTTP catálogo (`/api/sticker-packs*`, `/stickers*`, `/api/marketplace/stats`)
8
+
9
+ - **Latência p95**: `<= 750ms`
10
+ - **Latência p99**: `<= 1500ms`
11
+ - **Taxa de erro (5xx + timeout)**: `<= 2%` por janela de 5 minutos
12
+ - **Throughput alvo**: escalar linearmente com workers/processos sem aumento abrupto do p95
13
+
14
+ ### Classificação de stickers
15
+
16
+ - **Duração média do ciclo**: `<= 10s`
17
+ - **Throughput mínimo (assets classificados/min)**: `>= 300` (ajustar por hardware)
18
+ - **Backlog de fila (`sticker_reprocess_pending`)**: tendência de queda após picos; alerta se cresce por mais de 15 min
19
+
20
+ ## 2. Métricas instrumentadas
21
+
22
+ ### HTTP
23
+
24
+ - `omnizap_http_requests_total{route_group,method,status_class}`
25
+ - `omnizap_http_request_duration_ms{route_group,method,status_class}`
26
+ - `omnizap_http_slo_violation_total{route_group,method}`
27
+
28
+ `route_group` segmenta tráfego em:
29
+
30
+ - `catalog_api_public`
31
+ - `catalog_api_auth`
32
+ - `catalog_api_admin`
33
+ - `catalog_api_upload`
34
+ - `catalog_web`
35
+ - `catalog_data_asset`
36
+ - `catalog_user_profile`
37
+ - `marketplace_stats`
38
+ - `metrics`
39
+ - `other`
40
+
41
+ ### Classificação
42
+
43
+ - `omnizap_sticker_classification_cycle_duration_ms{status}`
44
+ - `omnizap_sticker_classification_cycle_total{status}`
45
+ - `omnizap_sticker_classification_assets_total{outcome}`
46
+ - `omnizap_queue_depth{queue}`
47
+
48
+ ## 3. Tracing mínimo
49
+
50
+ - Cada request HTTP agora recebe/propaga `X-Request-Id`.
51
+ - Se o cliente enviar `X-Request-Id`, o valor é reaproveitado.
52
+ - Sem header, o servidor gera UUID.
53
+
54
+ ## 4. Baseline de carga (script local)
55
+
56
+ Script: `scripts/sticker-catalog-loadtest.mjs`
57
+
58
+ Exemplo:
59
+
60
+ ```bash
61
+ node scripts/sticker-catalog-loadtest.mjs \
62
+ --base-url http://127.0.0.1:9102 \
63
+ --duration-seconds 60 \
64
+ --concurrency 40 \
65
+ --paths "/api/sticker-packs?limit=24&sort=popular,/api/sticker-packs/stats,/api/sticker-packs/creators?limit=25" \
66
+ --out /tmp/sticker-loadtest-report.json
67
+ ```
68
+
69
+ Interpretação rápida:
70
+
71
+ - `latency_ms.p95 <= 750` = SLO de latência cumprido
72
+ - `error_rate <= 0.02` = estabilidade aceitável
73
+ - `throughput_rps` = referência para comparar antes/depois de otimizações
74
+
75
+ ## 5. Gate de rollout sugerido
76
+
77
+ 1. Capturar baseline com carga atual.
78
+ 2. Aplicar mudança de arquitetura/índice/cache.
79
+ 3. Reexecutar carga com mesmos parâmetros.
80
+ 4. Aprovar rollout apenas se:
81
+ - p95 não piorar mais de 10%
82
+ - erro não subir acima de 2%
83
+ - backlog voltar ao patamar normal em até 15 min
@@ -0,0 +1,128 @@
1
+ # Sticker 10x Hardening And Rollout
2
+
3
+ ## Scope
4
+
5
+ This runbook covers phases 4-8 of the sticker-pack scale plan:
6
+
7
+ 1. ranking snapshot read path
8
+ 2. internal outbox/event consumer
9
+ 3. dedicated workers (classification/curation/rebuild)
10
+ 4. object storage delivery with secure URLs
11
+ 5. canary rollout, rollback, and final tuning
12
+
13
+ ## Feature Flags
14
+
15
+ Flags are stored in `feature_flag`:
16
+
17
+ - `enable_ranking_snapshot_read`
18
+ - `enable_domain_event_outbox`
19
+ - `enable_worker_dedicated_processes`
20
+ - `enable_object_storage_delivery`
21
+
22
+ ### Query Current Status
23
+
24
+ ```sql
25
+ SELECT flag_name, is_enabled, rollout_percent, updated_at
26
+ FROM feature_flag
27
+ WHERE flag_name IN (
28
+ 'enable_ranking_snapshot_read',
29
+ 'enable_domain_event_outbox',
30
+ 'enable_worker_dedicated_processes',
31
+ 'enable_object_storage_delivery'
32
+ )
33
+ ORDER BY flag_name;
34
+ ```
35
+
36
+ ### Update Rollout Percent
37
+
38
+ ```sql
39
+ UPDATE feature_flag
40
+ SET is_enabled = 1, rollout_percent = 25, updated_by = 'ops'
41
+ WHERE flag_name = 'enable_worker_dedicated_processes';
42
+ ```
43
+
44
+ ### Emergency Disable
45
+
46
+ ```sql
47
+ UPDATE feature_flag
48
+ SET is_enabled = 0, rollout_percent = 0, updated_by = 'ops'
49
+ WHERE flag_name IN (
50
+ 'enable_worker_dedicated_processes',
51
+ 'enable_object_storage_delivery',
52
+ 'enable_domain_event_outbox'
53
+ );
54
+ ```
55
+
56
+ ## Canary Sequence
57
+
58
+ 1. `enable_ranking_snapshot_read`: 10% -> 50% -> 100%
59
+ 2. `enable_domain_event_outbox`: 10% -> 50% -> 100%
60
+ 3. start dedicated worker processes and set `enable_worker_dedicated_processes`: 10% -> 50% -> 100%
61
+ 4. `enable_object_storage_delivery`: 5% -> 25% -> 100%
62
+
63
+ Promotion gate for each step:
64
+
65
+ - HTTP p95 within target
66
+ - queue backlog stable (`pending`, `failed`)
67
+ - outbox DLQ not growing unexpectedly
68
+ - no sustained error-rate increase
69
+
70
+ ## Dedicated Workers
71
+
72
+ Run workers as isolated processes:
73
+
74
+ ```bash
75
+ npm run worker:sticker:classification
76
+ npm run worker:sticker:curation
77
+ npm run worker:sticker:rebuild
78
+ ```
79
+
80
+ PM2 production profile includes these workers in `ecosystem.prod.config.cjs`.
81
+
82
+ ## 10x Validation
83
+
84
+ ### HTTP Stress
85
+
86
+ ```bash
87
+ npm run loadtest:stickers -- --base-url http://127.0.0.1:9102 --duration-seconds 120 --concurrency 200 --slo-ms 750
88
+ ```
89
+
90
+ ### Queue/Worker Validation
91
+
92
+ Monitor:
93
+
94
+ - `sticker_worker_tasks_pending`
95
+ - `sticker_worker_tasks_processing`
96
+ - `sticker_worker_tasks_failed`
97
+ - `domain_event_outbox_pending`
98
+ - `domain_event_outbox_failed`
99
+
100
+ Acceptance:
101
+
102
+ - failed queues remain near zero (transient spikes allowed)
103
+ - pending queues recover after load burst
104
+ - no monotonic growth in DLQ tables
105
+
106
+ ## Rollback Plan
107
+
108
+ 1. Disable `enable_object_storage_delivery`.
109
+ 2. Disable `enable_worker_dedicated_processes` (inline poller resumes).
110
+ 3. Disable `enable_domain_event_outbox` if event flow is unstable.
111
+ 4. Keep `enable_ranking_snapshot_read` enabled only if snapshot freshness is healthy.
112
+
113
+ Data safety notes:
114
+
115
+ - tasks/events are persisted in SQL queues
116
+ - failed terminal tasks/events are preserved in DLQ tables
117
+ - local disk read path remains fallback for sticker asset serving
118
+
119
+ ## Post-Rollout Tuning
120
+
121
+ Tune these env vars after baseline:
122
+
123
+ - `STICKER_WORKER_CLASSIFICATION_CADENCE_MS`
124
+ - `STICKER_WORKER_CURATION_CADENCE_MS`
125
+ - `STICKER_WORKER_REBUILD_CADENCE_MS`
126
+ - `STICKER_DEDICATED_WORKER_POLL_INTERVAL_MS`
127
+ - `STICKER_SCORE_SNAPSHOT_REFRESH_INTERVAL_MS`
128
+ - `STICKER_OBJECT_STORAGE_SIGNED_URL_TTL_SECONDS`
package/package.json CHANGED
@@ -1,45 +1,11 @@
1
1
  {
2
2
  "name": "@kaikybrofc/omnizap-system",
3
- "version": "2.2.5",
3
+ "version": "2.2.6",
4
4
  "description": "Sistema profissional de automação WhatsApp com tecnologia Baileys",
5
5
  "main": "index.js",
6
6
  "publishConfig": {
7
7
  "registry": "https://npm.pkg.github.com"
8
8
  },
9
- "files": [
10
- "app/**/*.js",
11
- "database/**/*.js",
12
- "database/migrations/**/*.sql",
13
- "public/**/*.html",
14
- "public/**/*.css",
15
- "public/**/*.js",
16
- "scripts/**/*.sh",
17
- "scripts/**/*.mjs",
18
- "ml/clip_classifier/adaptive_scoring.py",
19
- "ml/clip_classifier/classifier.py",
20
- "ml/clip_classifier/embedding_store.py",
21
- "ml/clip_classifier/env_loader.py",
22
- "ml/clip_classifier/llm_label_expander.py",
23
- "ml/clip_classifier/main.py",
24
- "ml/clip_classifier/similarity_engine.py",
25
- "ml/clip_classifier/requirements.txt",
26
- "ml/clip_classifier/README.md",
27
- "ml/clip_classifier/Dockerfile",
28
- "observability/**/*.yml",
29
- "observability/**/*.yaml",
30
- "observability/**/*.json",
31
- "observability/**/*.sql",
32
- "observability/**/*.cnf",
33
- "index.js",
34
- "docker-compose.yml",
35
- "ecosystem.prod.config.cjs",
36
- "eslint.config.js",
37
- ".env.example",
38
- "README.md",
39
- "RELEASE-v2.1.2.md",
40
- "LICENSE",
41
- "package-lock.json"
42
- ],
43
9
  "type": "module",
44
10
  "keywords": [
45
11
  "whatsapp",
@@ -52,6 +18,8 @@
52
18
  "author": "OmniZap Team",
53
19
  "license": "MIT",
54
20
  "scripts": {
21
+ "prepack": "npm run sync:npmignore",
22
+ "sync:npmignore": "node -e \"const fs=require('node:fs');fs.copyFileSync('.gitignore','.npmignore');console.log('synced .npmignore from .gitignore');\"",
55
23
  "start": "node index.js",
56
24
  "dev": "node index.js",
57
25
  "db:init": "node database/init.js",