@praeko/cli 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/CHANGELOG.md +10 -2
  2. package/README.md +2 -2
  3. package/dist/chunks/{adaptadores-X3NUBMLT.mjs → adaptadores-73I3A475.mjs} +5 -5
  4. package/dist/chunks/{buffer-5TUDOBVN.mjs → buffer-7UXG3MU4.mjs} +5 -5
  5. package/dist/chunks/{chunk-QDZLRBTP.mjs → chunk-FZ2EWUHA.mjs} +133 -57
  6. package/dist/chunks/{chunk-SAHLBARV.mjs → chunk-K5RD3QB7.mjs} +12 -12
  7. package/dist/chunks/{chunk-Z325UM27.mjs → chunk-SL4UDIFM.mjs} +1 -1
  8. package/dist/chunks/{chunk-IZEX746E.mjs → chunk-SPELUAPP.mjs} +15 -15
  9. package/dist/chunks/chunk-SSYEOFS5.mjs +56 -0
  10. package/dist/chunks/{chunk-LLXYUCTT.mjs → chunk-TDQGKU7I.mjs} +9 -8
  11. package/dist/chunks/{chunk-UK3UKRPR.mjs → chunk-V3GB2IUZ.mjs} +20 -3
  12. package/dist/chunks/{instagram-GNY73ESY.mjs → instagram-3P4ROM45.mjs} +1 -1
  13. package/dist/chunks/{instagram-meta-COY6BTNX.mjs → instagram-meta-ANEWEJV7.mjs} +2 -1
  14. package/dist/chunks/{local-EUJHQ76T.mjs → local-QWOY7WI5.mjs} +3 -3
  15. package/dist/chunks/{registro-FL2CVMYM.mjs → registro-2EE55WTS.mjs} +1 -1
  16. package/dist/chunks/{remoto-OAB5Y2MS.mjs → remoto-HPELHT4F.mjs} +2 -2
  17. package/dist/chunks/{tiktok-QOUHOWDU.mjs → tiktok-DVG5JNJE.mjs} +1 -1
  18. package/dist/chunks/{tiktok-api-36DV2H43.mjs → tiktok-api-2CYJNS73.mjs} +1 -1
  19. package/dist/chunks/{youtube-RZ4XTUZW.mjs → youtube-EZL4SNMO.mjs} +4 -2
  20. package/dist/chunks/{youtube-analytics-3KIW2ZIW.mjs → youtube-analytics-74ZDYCY6.mjs} +2 -2
  21. package/dist/chunks/{youtube-api-Z2RP5D2V.mjs → youtube-api-QVEOU2L7.mjs} +3 -1
  22. package/dist/chunks/{youtube-comunidade-OAK5GGK4.mjs → youtube-comunidade-GJBG2XR7.mjs} +2 -2
  23. package/dist/praeko.mjs +851 -8
  24. package/package.json +5 -5
  25. package/dist/arauto.mjs +0 -11
  26. package/dist/chunks/chunk-6YARHJZ2.mjs +0 -56
  27. package/dist/chunks/chunk-XQO6MI7C.mjs +0 -851
package/CHANGELOG.md CHANGED
@@ -2,8 +2,16 @@
2
2
 
3
3
  Segue [Keep a Changelog](https://keepachangelog.com/pt-BR/1.1.0/) e [SemVer](https://semver.org/lang/pt-BR/).
4
4
 
5
- ## [0.1.0] - não publicado
5
+ ## [0.2.0] - 2026-11-07
6
+
7
+ Sem mudança de comportamento: o código da CLI (e do `@praeko/core` que ela embute) agora é TypeScript (`strict`), empacotado pelo esbuild como antes. Mesmo `dist/praeko.mjs`, zero dependências.
8
+
9
+ ## [0.1.1] - 2026-11-07
10
+
11
+ - Um só comando, `praeko` (o alias `arauto` saiu do pacote). Com dois comandos o `npx @praeko/cli` não sabia qual rodar e falhava com "could not determine executable to run"; agora `npx @praeko/cli --help` funciona. Quem usou a 0.1.0 pode rodar `npx -p @praeko/cli praeko`.
12
+
13
+ ## [0.1.0] - 2026-11-07
6
14
 
7
15
  Primeira versão preparada para o npm.
8
- - Pacote `@praeko/cli`: comandos `praeko` e `arauto`, empacotado em `dist/` com o core e o SDK embutidos (sem dependências); `--version`; `praeko studio` avisa quando o Studio não está instalado.
16
+ - Pacote `@praeko/cli`: comando `praeko`, empacotado em `dist/` com o core e o SDK embutidos (sem dependências); `--version`; `praeko studio` avisa quando o Studio não está instalado.
9
17
  - Licença MIT (arquivo `LICENSE` no pacote) e repositório público de código-cliente (`Praeko/praeko-js`).
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @praeko/cli
2
2
 
3
- Linha de comando do [Praeko](https://praeko.app): publica e agenda vídeos curtos no YouTube, Instagram e TikTok, e fala com a API do serviço (posts, fila, canais, chaves, insights). Comando `praeko` (o antigo `arauto` continua como apelido). Node 22 ou superior; `ffmpeg`/`ffprobe` só para os comandos que mexem em capa e vídeo.
3
+ Linha de comando do [Praeko](https://praeko.app): publica e agenda vídeos curtos no YouTube, Instagram e TikTok, e fala com a API do serviço (posts, fila, canais, chaves, insights). Comando `praeko` (antes chamado `arauto`). Node 22 ou superior; `ffmpeg`/`ffprobe` só para os comandos que mexem em capa e vídeo.
4
4
 
5
5
  ## Instalação
6
6
 
@@ -52,7 +52,7 @@ A chave vem de `PRAEKO_KEY`. Sem ela, a CLI procura no Chaveiro do macOS (só no
52
52
 
53
53
  ## Links
54
54
 
55
- Documentação: <https://praeko.app/docs> · Código e issues: <https://github.com/Praeko/praeko-js> · SDK: [`@praeko/sdk`](https://www.npmjs.com/package/@praeko/sdk)
55
+ Documentação: <https://praeko.app/docs> · Código: <https://github.com/Praeko/praeko-js> · Suporte: contato@praeko.app · SDK: [`@praeko/sdk`](https://www.npmjs.com/package/@praeko/sdk)
56
56
 
57
57
  ## Licença
58
58
 
@@ -1,15 +1,15 @@
1
1
  import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
2
2
  import {
3
3
  buffer_exports
4
- } from "./chunk-SAHLBARV.mjs";
4
+ } from "./chunk-K5RD3QB7.mjs";
5
5
  import {
6
6
  local_exports
7
- } from "./chunk-IZEX746E.mjs";
8
- import "./chunk-6YARHJZ2.mjs";
7
+ } from "./chunk-SPELUAPP.mjs";
8
+ import "./chunk-SSYEOFS5.mjs";
9
9
  import {
10
10
  remoto_exports
11
- } from "./chunk-QDZLRBTP.mjs";
12
- import "./chunk-Z325UM27.mjs";
11
+ } from "./chunk-FZ2EWUHA.mjs";
12
+ import "./chunk-SL4UDIFM.mjs";
13
13
  import "./chunk-LCFK6X55.mjs";
14
14
  export {
15
15
  buffer_exports as buffer,
@@ -2,11 +2,11 @@ import { createRequire as __cr } from 'node:module'; const require = __cr(import
2
2
  import {
3
3
  REF_CHAVE,
4
4
  criar
5
- } from "./chunk-SAHLBARV.mjs";
6
- import "./chunk-IZEX746E.mjs";
7
- import "./chunk-6YARHJZ2.mjs";
8
- import "./chunk-QDZLRBTP.mjs";
9
- import "./chunk-Z325UM27.mjs";
5
+ } from "./chunk-K5RD3QB7.mjs";
6
+ import "./chunk-SPELUAPP.mjs";
7
+ import "./chunk-SSYEOFS5.mjs";
8
+ import "./chunk-FZ2EWUHA.mjs";
9
+ import "./chunk-SL4UDIFM.mjs";
10
10
  import "./chunk-LCFK6X55.mjs";
11
11
  export {
12
12
  REF_CHAVE,
@@ -1,12 +1,12 @@
1
1
  import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
2
2
  import {
3
3
  validarPedido
4
- } from "./chunk-Z325UM27.mjs";
4
+ } from "./chunk-SL4UDIFM.mjs";
5
5
  import {
6
6
  __export
7
7
  } from "./chunk-LCFK6X55.mjs";
8
8
 
9
- // ../core/src/adaptadores/remoto.mjs
9
+ // ../core/src/adaptadores/remoto.ts
10
10
  var remoto_exports = {};
11
11
  __export(remoto_exports, {
12
12
  REF_CHAVE: () => REF_CHAVE,
@@ -14,14 +14,19 @@ __export(remoto_exports, {
14
14
  itemDoContrato: () => itemDoContrato
15
15
  });
16
16
 
17
- // ../sdk/src/legacy.mjs
17
+ // ../sdk/dist/legacy.js
18
18
  var enc = (x) => encodeURIComponent(x);
19
19
  function fonte(x) {
20
- if (x && typeof x.read === "function" && typeof x.size === "number") return x;
21
- if (x && typeof x.ler === "function" && typeof x.tamanho === "number") return { size: x.tamanho, read: (i, n) => x.ler(i, n) };
22
- if (typeof Blob !== "undefined" && x instanceof Blob) return { size: x.size, read: async (i, n) => new Uint8Array(await x.slice(i, i + n).arrayBuffer()) };
20
+ const a = x;
21
+ if (a && typeof a.read === "function" && typeof a.size === "number")
22
+ return a;
23
+ if (a && typeof a.ler === "function" && typeof a.tamanho === "number")
24
+ return { size: a.tamanho, read: (i, n) => a.ler(i, n) };
25
+ if (typeof Blob !== "undefined" && x instanceof Blob)
26
+ return { size: x.size, read: async (i, n) => new Uint8Array(await x.slice(i, i + n).arrayBuffer()) };
23
27
  const u8 = x instanceof Uint8Array ? x : x instanceof ArrayBuffer ? new Uint8Array(x) : null;
24
- if (!u8) throw new TypeError("fonte inv\xE1lida: use Uint8Array, ArrayBuffer, Blob ou { tamanho, ler(inicio, n) }");
28
+ if (!u8)
29
+ throw new TypeError("fonte inv\xE1lida: use Uint8Array, ArrayBuffer, Blob ou { tamanho, ler(inicio, n) }");
25
30
  return { size: u8.length, read: async (i, n) => u8.subarray(i, i + n) };
26
31
  }
27
32
  function addLegacy(a) {
@@ -37,7 +42,8 @@ function addLegacy(a) {
37
42
  /** Uploads a whole media (legacy shapes: { id, … } with Portuguese fields). Prefer media.upload. */
38
43
  enviar: async (origem, { tipo, nome, mime, parte = 16 * 1024 * 1024, aoProgredir } = {}) => {
39
44
  const s = fonte(origem);
40
- if (!tipo || !nome || !mime) throw new Error("midia.enviar: informe tipo, nome e mime");
45
+ if (!tipo || !nome || !mime)
46
+ throw new Error("midia.enviar: informe tipo, nome e mime");
41
47
  if (s.size <= 32 * 1024 * 1024) {
42
48
  const m2 = await a.midia.iniciar({ tipo, nome, mime, tamanho: s.size, simples: true });
43
49
  const r = await a.midia.enviarConteudo(m2.id, await s.read(0, s.size));
@@ -105,7 +111,7 @@ function addLegacy(a) {
105
111
  a.saude = () => L("GET", "/saude", { semChave: true });
106
112
  }
107
113
 
108
- // ../sdk/src/index.mjs
114
+ // ../sdk/dist/index.js
109
115
  var PraekoError = class extends Error {
110
116
  constructor(status, code, message, details) {
111
117
  super(message);
@@ -131,23 +137,29 @@ var SIMPLE_MAX = 32 * 1024 * 1024;
131
137
  var DEFAULT_PART = 16 * 1024 * 1024;
132
138
  var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
133
139
  function asSource(x) {
134
- if (x && typeof x.read === "function" && typeof x.size === "number") return x;
135
- if (x && typeof x.ler === "function" && typeof x.tamanho === "number") return { size: x.tamanho, read: (i, n) => x.ler(i, n) };
136
- if (typeof Blob !== "undefined" && x instanceof Blob) return { size: x.size, read: async (i, n) => new Uint8Array(await x.slice(i, i + n).arrayBuffer()) };
140
+ const a = x;
141
+ if (a && typeof a.read === "function" && typeof a.size === "number")
142
+ return a;
143
+ if (a && typeof a.ler === "function" && typeof a.tamanho === "number")
144
+ return { size: a.tamanho, read: (i, n) => a.ler(i, n) };
145
+ if (typeof Blob !== "undefined" && x instanceof Blob)
146
+ return { size: x.size, read: async (i, n) => new Uint8Array(await x.slice(i, i + n).arrayBuffer()) };
137
147
  const u8 = x instanceof Uint8Array ? x : x instanceof ArrayBuffer ? new Uint8Array(x) : null;
138
- if (!u8) throw new TypeError("invalid source: use Uint8Array, ArrayBuffer, Blob or { size, read(start, n) }");
148
+ if (!u8)
149
+ throw new TypeError("invalid source: use Uint8Array, ArrayBuffer, Blob or { size, read(start, n) }");
139
150
  return { size: u8.length, read: async (i, n) => u8.subarray(i, i + n) };
140
151
  }
141
152
  var ref = (id) => encodeURIComponent(id);
142
153
  var idem = (o) => o && o.idempotencyKey ? { idempotencyKey: o.idempotencyKey } : {};
143
154
  var uuid = () => globalThis.crypto?.randomUUID ? globalThis.crypto.randomUUID() : `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}-${Math.random().toString(36).slice(2)}`;
144
155
  var Praeko = class {
145
- // `cookies: true` (browser, same origin): no key; the panel session cookie authenticates.
146
- /** @param {{ url: string, key?: string, cookies?: boolean, chave?: string, fetch?: typeof fetch, retries?: number, wait?: (ms:number)=>Promise<void> }} o (`chave`, `tentativas`, `espera`: deprecated names) */
147
- constructor(o = {}) {
156
+ constructor(o) {
157
+ o = o ?? {};
148
158
  const { url, key = o.chave, cookies = false, fetch: f = globalThis.fetch, retries = o.tentativas ?? 3, wait = o.espera ?? sleep } = o;
149
- if (!url) throw new Error("Praeko: missing service url (falta a url do servi\xE7o)");
150
- if (!key && !cookies) throw new Error("Praeko: missing API key praeko_\u2026 (falta a chave)");
159
+ if (!url)
160
+ throw new Error("Praeko: missing service url (falta a url do servi\xE7o)");
161
+ if (!key && !cookies)
162
+ throw new Error("Praeko: missing API key praeko_\u2026 (falta a chave)");
151
163
  this.url = String(url).replace(/\/+$/, "");
152
164
  this._key = key;
153
165
  this._cookies = !key && cookies;
@@ -166,7 +178,8 @@ var Praeko = class {
166
178
  /** Uploads a whole file (one PUT or parts, depending on size). Returns the ready media { id, … }. */
167
179
  upload: async (source, { type, name, mime, part = DEFAULT_PART, onProgress } = {}) => {
168
180
  const f2 = asSource(source);
169
- if (!type || !name || !mime) throw new Error("media.upload: provide type, name and mime");
181
+ if (!type || !name || !mime)
182
+ throw new Error("media.upload: provide type, name and mime");
170
183
  if (f2.size <= SIMPLE_MAX) {
171
184
  const m2 = await a.media.create({ type, name, mime, size: f2.size, simple: true });
172
185
  const r = await a.media.uploadContent(m2.id, await f2.read(0, f2.size));
@@ -258,6 +271,10 @@ var Praeko = class {
258
271
  get: () => a._req("GET", "/v1/preferences"),
259
272
  put: ({ theme, language, notifications } = {}) => a._req("PUT", "/v1/preferences", { body: { theme, language, notifications } })
260
273
  };
274
+ this.notifications = {
275
+ list: ({ unread, limit, cursor } = {}) => a._req("GET", "/v1/notifications", { query: { unread, limit, cursor } }),
276
+ markRead: ({ ids, all } = {}) => a._req("POST", "/v1/notifications/read", { body: { ids, all } })
277
+ };
261
278
  this.webhooks = {
262
279
  list: () => a._req("GET", "/v1/webhooks"),
263
280
  create: ({ url: url2, events, active, idempotencyKey } = {}) => a._req("POST", "/v1/webhooks", { body: { url: url2, events, active }, ...idem({ idempotencyKey }) }),
@@ -291,16 +308,19 @@ var Praeko = class {
291
308
  /**
292
309
  * One HTTP call. `legacy: true` (only for the deprecated Portuguese aliases) skips the `X-Praeko-Contract: en` header, so the shared paths
293
310
  * (/v1/posts, /v1/insights, /v1/youtube/videos) answer in the old shape.
311
+ * @internal
294
312
  */
295
- async _req(method, path, { body, bytes, query, retry = method === "GET", noKey = false, legacy = false, idempotencyKey } = {}) {
296
- if (idempotencyKey) {
313
+ async _req(method, path, { body, bytes, query, retry = method === "GET", noKey = false, legacy = false, idempotencyKey: idemOpt } = {}) {
314
+ let idempotencyKey;
315
+ if (idemOpt) {
297
316
  retry = true;
298
- idempotencyKey = idempotencyKey === true ? uuid() : String(idempotencyKey);
317
+ idempotencyKey = idemOpt === true ? uuid() : String(idemOpt);
299
318
  }
300
319
  const q = query ? Object.entries(query).filter(([, v]) => v !== void 0 && v !== null && v !== "").map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&") : "";
301
320
  const url = `${this.url}${path}${q ? "?" + q : ""}`;
302
321
  const init = { method, headers: { ...noKey || this._cookies ? {} : { authorization: `Bearer ${this._key}` }, ...legacy ? {} : { "x-praeko-contract": "en" } } };
303
- if (idempotencyKey) init.headers["idempotency-key"] = idempotencyKey;
322
+ if (idempotencyKey)
323
+ init.headers["idempotency-key"] = idempotencyKey;
304
324
  if (bytes !== void 0) {
305
325
  init.body = bytes;
306
326
  init.headers["content-type"] = "application/octet-stream";
@@ -310,7 +330,8 @@ var Praeko = class {
310
330
  }
311
331
  let last;
312
332
  for (let i = 0; i < (retry ? this._retries : 1); i++) {
313
- if (i) await this._wait(Math.min(8e3, 500 * 2 ** i));
333
+ if (i)
334
+ await this._wait(Math.min(8e3, 500 * 2 ** i));
314
335
  let r;
315
336
  try {
316
337
  r = await this._fetch(url, init);
@@ -324,16 +345,18 @@ var Praeko = class {
324
345
  j = txt ? JSON.parse(txt) : null;
325
346
  } catch {
326
347
  }
327
- if (r.ok) return j;
348
+ if (r.ok)
349
+ return j;
328
350
  const e = j?.error ?? (j?.erro && { code: j.erro.codigo, message: j.erro.mensagem, details: j.erro.detalhes });
329
351
  last = new PraekoError(r.status, e?.code || "http_error", e?.message || `HTTP ${r.status}`, e?.details);
330
- if (!(r.status === 429 || r.status >= 500)) throw last;
352
+ if (!(r.status === 429 || r.status >= 500))
353
+ throw last;
331
354
  }
332
355
  throw last;
333
356
  }
334
357
  };
335
358
 
336
- // ../sdk/src/node.mjs
359
+ // ../sdk/dist/node.js
337
360
  import { closeSync, fstatSync, openSync, readSync } from "node:fs";
338
361
  import { basename, extname } from "node:path";
339
362
  var MIME = { ".mp4": "video/mp4", ".mov": "video/quicktime", ".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg" };
@@ -351,16 +374,18 @@ function fileSource(path) {
351
374
  async function uploadFile(cliente, path, { type, onProgress, tipo, aoProgredir } = {}) {
352
375
  const f = fileSource(path);
353
376
  try {
354
- if (!f.mime) throw new Error(`n\xE3o sei o tipo de ${path} (use .mp4, .mov, .png ou .jpg)`);
377
+ if (!f.mime)
378
+ throw new Error(`n\xE3o sei o tipo de ${path} (use .mp4, .mov, .png ou .jpg)`);
355
379
  const t = type || tipo || (f.mime.startsWith("video/") ? "video" : "cover"), cb = onProgress || aoProgredir;
356
- if (cliente.media?.upload) return await cliente.media.upload(f, { type: t === "capa" ? "cover" : t, name: f.name, mime: f.mime, onProgress: cb });
380
+ if (cliente.media?.upload)
381
+ return await cliente.media.upload(f, { type: t === "capa" ? "cover" : t, name: f.name, mime: f.mime, onProgress: cb });
357
382
  return await cliente.midia.enviar(f, { tipo: t === "cover" ? "capa" : t, nome: f.name, mime: f.mime, aoProgredir: cb });
358
383
  } finally {
359
384
  f.close();
360
385
  }
361
386
  }
362
387
 
363
- // ../sdk/src/contract.mjs
388
+ // ../sdk/dist/contract.js
364
389
  var ROUTES = [
365
390
  { key: "health", method: "GET", path: "/health", legacy: "/saude", scope: "public", summary: "Liveness probe" },
366
391
  { key: "health.v1", method: "GET", path: "/v1/health", legacy: "/v1/saude", scope: "public", summary: "Liveness probe (versioned path)" },
@@ -392,7 +417,7 @@ var ROUTES = [
392
417
  { key: "posts.adopt", method: "POST", path: "/v1/posts/adopt", legacy: "/v1/posts/adotar", scope: "write", body: "json", summary: "Register a Reel already scheduled in Buffer (does not call Buffer)" },
393
418
  { key: "posts.list", method: "GET", path: "/v1/posts", legacy: "/v1/posts", scope: "read", summary: "List posts (filters: state, channel, tag)" },
394
419
  { key: "posts.get", method: "GET", path: "/v1/posts/:id", legacy: "/v1/posts/:id", scope: "read", summary: "One post with its items and actions" },
395
- { key: "posts.update", method: "PATCH", path: "/v1/posts/:id", legacy: "/v1/posts/:id", scope: "write", body: "json", summary: "Edit title, texts, media, tags, items; move to draft/queue" },
420
+ { key: "posts.update", method: "PATCH", path: "/v1/posts/:id", legacy: "/v1/posts/:id", scope: "write", body: "json", summary: "Edit title, texts, media, tags, items; move to draft/queue; reschedule/edit a YouTube video already uploaded and still scheduled" },
396
421
  { key: "posts.cancel", method: "DELETE", path: "/v1/posts/:id", legacy: "/v1/posts/:id", scope: "write", summary: "Cancel the pending items of a post" },
397
422
  { key: "posts.publishNow", method: "POST", path: "/v1/posts/:id/publish-now", legacy: "/v1/posts/:id/publicar-agora", scope: "write", summary: "Take pending items out of the queue and publish now (?network=)" },
398
423
  { key: "posts.youtubeSchedule", method: "POST", path: "/v1/posts/:id/youtube/schedule", legacy: "/v1/posts/:id/youtube/agendar", scope: "write", body: "json", summary: "Schedule publication of a video already uploaded to YouTube (private)" },
@@ -425,7 +450,9 @@ var ROUTES = [
425
450
  { key: "usage.get", method: "GET", path: "/v1/usage", legacy: "/v1/usage", scope: "read", summary: "API usage of this workspace over the last `days` (1-30, default 7, UTC): total, succeeded (< 400), errors, p95 duration, peak requests in one minute vs the per-minute limit, `byDay`, `byHour` (only with days=1: the last 24 hours) and `byKey` (`session` = the panel)" },
426
451
  { key: "usage.log", method: "GET", path: "/v1/usage/log", legacy: "/v1/usage/log", scope: "read", summary: "Request log, newest first, kept 30 days; metadata only (method, route template without ids, status, duration, key), never bodies, headers or query. Filters: `key` (id or `session`), `status` (`2xx`|`3xx`|`4xx`|`5xx` or a code), `route` (substring), `method`, `days` (1-30); `limit` (1-200, default 50) and `cursor` (the `next` of the previous page)" },
427
452
  { key: "preferences.get", method: "GET", path: "/v1/preferences", legacy: "/v1/preferences", scope: "read", summary: "Panel preferences of the signed-in user in this workspace (an API key reads the workspace row): `theme` (light, dark, system), `language` (pt-BR, en) and `notifications` per event (`postSent`, `postFailed`, `scheduledFail`, `weeklySummary`, `newComment`) with `email` and `panel` flags. Missing values come back as the defaults (dark, pt-BR, no e-mail, panel on)" },
428
- { key: "preferences.put", method: "PUT", path: "/v1/preferences", legacy: "/v1/preferences", scope: "write", body: "json", summary: "Change preferences; partial (only the fields sent change) and strict (unknown field or value is 422 invalid_field). Returns the full preferences. `postSent`, `postFailed` and `scheduledFail` e-mails are sent by the server when `email` is true; `weeklySummary` and `newComment` are stored but no sender exists yet" },
453
+ { key: "preferences.put", method: "PUT", path: "/v1/preferences", legacy: "/v1/preferences", scope: "write", body: "json", summary: "Change preferences; partial (only the fields sent change) and strict (unknown field or value is 422 invalid_field). Returns the full preferences. With `email` true the server e-mails `postSent`, `postFailed`, `scheduledFail`, the `weeklySummary` (Mondays 08:00 America/Sao_Paulo) and `newComment` (batched, at most one e-mail per hour); with `panel` true it adds the event to the notification center (`GET /v1/notifications`)" },
454
+ { key: "notifications.list", method: "GET", path: "/v1/notifications", legacy: "/v1/notifications", scope: "read", summary: "In-panel notification center of the signed-in user in this workspace (the bell), newest first: `notifications` (`id`, `event` = postSent, postFailed, scheduledFail, weeklySummary, newComment, `data` with the facts of the event, `link` to where it leads, `read`, `readAt`, `createdAt`), `unreadCount` and `next`. A notification exists only when the user's `panel` preference for that event was on when it happened. Filters: `unread` (true), `limit` (1-50, default 20) and `cursor` (the `next` of the previous page). Kept 30 days. An API key has no user and reads an empty list" },
455
+ { key: "notifications.read", method: "POST", path: "/v1/notifications/read", legacy: "/v1/notifications/read", scope: "write", body: "json", summary: "Mark notifications as read: `ids` (up to 100) or `all: true`. Ids that are not yours or do not exist are ignored and marking again is harmless. Returns `marked` (how many changed) and the new `unreadCount`" },
429
456
  { key: "webhooks.list", method: "GET", path: "/v1/webhooks", legacy: "/v1/webhooks", scope: "admin", summary: "Outgoing webhooks of this workspace (never the secret: only `secretHint`), with the delivery success of the last 7 days (`recentDeliveries`) and the subscribable `events`" },
430
457
  { key: "webhooks.create", method: "POST", path: "/v1/webhooks", legacy: "/v1/webhooks", scope: "admin", body: "json", summary: "Create a webhook: `url` (https, public host; private IPs, localhost and cloud metadata are refused with unsafe_url), `events` (post.sent, post.failed, post.scheduled-fail; default the first two), `active`. The signing `secret` is returned ONCE. Max 10 per workspace" },
431
458
  { key: "webhooks.get", method: "GET", path: "/v1/webhooks/:id", legacy: "/v1/webhooks/:id", scope: "admin", summary: "One webhook (no secret)" },
@@ -443,14 +470,21 @@ var ROUTES = [
443
470
  ];
444
471
  var toRegExp = (tpl) => new RegExp("^" + tpl.replace(/:[A-Za-z_]+/g, "([^/]+)").replace(/\//g, "\\/") + "$");
445
472
  var NEW_RE = ROUTES.map((r) => ({ r, re: toRegExp(r.path) }));
446
- var OLD_RE = ROUTES.filter((r) => r.legacy).map((r) => ({ r, re: toRegExp(r.legacy) }));
447
- var KEYS = [
473
+ var OLD_RE = ROUTES.flatMap((r) => r.legacy ? [{ r, re: toRegExp(r.legacy) }] : []);
474
+ var KEY_ROWS = [
448
475
  ["abs", "absolute"],
449
476
  ["acao", "action"],
450
477
  ["acoes", "actions"],
451
478
  ["adotado", "adopted"],
452
479
  ["agregadoAtualizadoEm", "aggregateUpdatedAt"],
453
480
  ["agregadoBuffer", "bufferAggregate"],
481
+ ["aplicado", "applied"],
482
+ ["notas", "notes", "res"],
483
+ ["tagsVideo", "videoTags", "req"],
484
+ ["campo", "field"],
485
+ ["campos", "fields"],
486
+ ["naoAplicados", "notApplied"],
487
+ ["resultadosItens", "itemResults"],
454
488
  ["alcance", "reach"],
455
489
  ["anterior", "previous"],
456
490
  ["antes", "before"],
@@ -671,6 +705,7 @@ var KEYS = [
671
705
  ["metodo", "method"],
672
706
  ["rota", "route"],
673
707
  ["duracaoMs", "durationMs"],
708
+ ["duracao", "duration"],
674
709
  ["largura", "width"],
675
710
  ["altura", "height"],
676
711
  ["chaveNome", "keyName"],
@@ -694,6 +729,19 @@ var KEYS = [
694
729
  ["ideias", "ideas"],
695
730
  ["nota", "note"],
696
731
  ["grupo", "group"],
732
+ // central de avisos (/v1/notifications)
733
+ ["naoLidos", "unreadCount"],
734
+ ["lido", "read"],
735
+ ["lido_em", "readAt"],
736
+ ["dados", "data"],
737
+ ["marcados", "marked"],
738
+ ["vaiTentar", "willRetry"],
739
+ ["quantidade", "count"],
740
+ ["enviados", "sent"],
741
+ ["falhas", "failed"],
742
+ ["esperando", "waiting"],
743
+ ["topTitulo", "topTitle"],
744
+ ["topRede", "topNetwork"],
697
745
  // retenção, impressões e CTR (/v1/insights/retention)
698
746
  ["retencao", "retention"],
699
747
  ["curva", "curve"],
@@ -706,9 +754,11 @@ var KEYS = [
706
754
  ["desde", "since"],
707
755
  ["pontos", "points"],
708
756
  ["coletadoEm", "collectedAt"]
709
- ].filter((x, i, a) => a.findIndex((y) => y[0] === x[0]) === i);
757
+ ];
758
+ var KEYS = KEY_ROWS.filter((x, i, a) => a.findIndex((y) => y[0] === x[0]) === i);
710
759
  var SCOPED_KEYS = { reorganizados: [["de", "from"], ["para", "to"]], leitura48h: [["destinoId", "itemId"]] };
711
- var SAME_KEYS = /* @__PURE__ */ new Set(["id", "ctr", "item", "url", "views", "tags", "meta", "host", "token", "userId", "ok", "total", "pct", "manual", "custom", "studio", "video", "mime", "etag", "avatar", "youtube", "instagram", "tiktok", "publishAt", "parentId", "permalink", "thumbnail", "posts", "topPosts", "videos", "redirect", "chunkMB", "via", "videoId", "postId", "threadId", "limit", "max", "publishId", "status", "p95Ms", "cursor", "webhooks", "theme", "language", "notifications", "email", "panel", "postSent", "postFailed", "scheduledFail", "weeklySummary", "newComment"]);
760
+ var SAME_LIST = ["id", "ctr", "item", "url", "views", "tags", "meta", "host", "token", "userId", "ok", "total", "pct", "manual", "custom", "studio", "video", "mime", "etag", "avatar", "youtube", "instagram", "tiktok", "publishAt", "parentId", "permalink", "thumbnail", "posts", "topPosts", "videos", "redirect", "chunkMB", "via", "videoId", "postId", "threadId", "limit", "max", "publishId", "status", "p95Ms", "cursor", "webhooks", "theme", "language", "notifications", "email", "panel", "postSent", "postFailed", "scheduledFail", "weeklySummary", "newComment", "unread", "ids", "all", "link", "topViews"];
761
+ var SAME_KEYS = new Set(SAME_LIST);
712
762
  var VALUES = {
713
763
  estado: [
714
764
  ["rascunho", "draft"],
@@ -786,22 +836,27 @@ var index = (rows, dir) => {
786
836
  const mk = (kind) => {
787
837
  const m = /* @__PURE__ */ new Map();
788
838
  for (const [pt, en, only] of rows) {
789
- if (only && only !== kind) continue;
839
+ if (only && only !== kind)
840
+ continue;
790
841
  if (dir === "toEN") {
791
- if (!m.has(pt)) m.set(pt, en);
792
- } else if (!m.has(en)) m.set(en, pt);
842
+ if (!m.has(pt))
843
+ m.set(pt, en);
844
+ } else if (!m.has(en))
845
+ m.set(en, pt);
793
846
  }
794
847
  return m;
795
848
  };
796
849
  return { req: mk("req"), res: mk("res") };
797
850
  };
798
- var KEY_IDX = { toEN: index(KEYS, "toEN"), toPT: index(KEYS, "toPT") };
799
- var VAL_IDX = Object.fromEntries(Object.entries(VALUES).map(([k, rows]) => [k, { toEN: index(rows, "toEN"), toPT: index(rows, "toPT") }]));
800
- var SKEY_IDX = Object.fromEntries(Object.entries(SCOPED_KEYS).map(([k, rows]) => [k, { toEN: index(rows, "toEN"), toPT: index(rows, "toPT") }]));
801
- var SVAL_IDX = Object.fromEntries(Object.entries(SCOPED_VALUES).map(([p, fields]) => [p, Object.fromEntries(Object.entries(fields).map(([k, rows]) => [k, { toEN: index(rows, "toEN"), toPT: index(rows, "toPT") }]))]));
851
+ var both = (rows) => ({ toEN: index(rows, "toEN"), toPT: index(rows, "toPT") });
852
+ var KEY_IDX = both(KEYS);
853
+ var VAL_IDX = Object.fromEntries(Object.entries(VALUES).map(([k, rows]) => [k, both(rows)]));
854
+ var SKEY_IDX = Object.fromEntries(Object.entries(SCOPED_KEYS).map(([k, rows]) => [k, both(rows)]));
855
+ var SVAL_IDX = Object.fromEntries(Object.entries(SCOPED_VALUES).map(([p, fields]) => [p, Object.fromEntries(Object.entries(fields).map(([k, rows]) => [k, both(rows)]))]));
802
856
  var PT_ONLY_KEYS = new Set(KEYS.filter(([pt, en]) => pt !== en).map(([pt]) => pt));
803
857
  var EN_KEYS = new Set(KEYS.map(([, en]) => en));
804
- for (const k of EN_KEYS) PT_ONLY_KEYS.delete(k);
858
+ for (const k of EN_KEYS)
859
+ PT_ONLY_KEYS.delete(k);
805
860
  var EN_ALSO = new Set(Object.entries(VALUES).flatMap(([campo, rows]) => rows.map((r) => `${campo}|${r[1]}`)));
806
861
  var PtKeyError = class extends Error {
807
862
  constructor(key, value) {
@@ -816,28 +871,34 @@ for (const [campo, rows] of Object.entries(VALUES)) {
816
871
  const en = new Set(rows.map((r) => r[1]));
817
872
  PT_ONLY_VALUES[campo] = new Set(rows.filter((r) => !r[2] || r[2] === "req").map((r) => r[0]).filter((pt) => !en.has(pt)));
818
873
  }
819
- for (const [pai, campos] of Object.entries(SCOPED_VALUES)) for (const [campo, rows] of Object.entries(campos)) {
820
- const en = new Set(rows.map((r) => r[1]));
821
- PT_ONLY_VALUES[`${pai}.${campo}`] ||= new Set(rows.map((r) => r[0]).filter((pt) => !en.has(pt)));
822
- }
874
+ for (const [pai, campos] of Object.entries(SCOPED_VALUES))
875
+ for (const [campo, rows] of Object.entries(campos)) {
876
+ const en = new Set(rows.map((r) => r[1]));
877
+ PT_ONLY_VALUES[`${pai}.${campo}`] ||= new Set(rows.map((r) => r[0]).filter((pt) => !en.has(pt)));
878
+ }
823
879
  function translate(value, o, container = null, own = null) {
824
880
  const { dir, kind } = o;
825
- if (Array.isArray(value)) return value.map((x) => translate(x, o, container, own));
881
+ if (Array.isArray(value))
882
+ return value.map((x) => translate(x, o, container, own));
826
883
  if (value && typeof value === "object") {
827
- const out = {}, scoped = SKEY_IDX[own]?.[dir]?.[kind];
884
+ const out = {}, scoped = own === null ? void 0 : SKEY_IDX[own]?.[dir]?.[kind];
828
885
  for (const [k, v] of Object.entries(value)) {
829
886
  const hit = scoped?.get(k) ?? KEY_IDX[dir][kind].get(k);
830
- if (hit === void 0 && o.strict && dir === "toPT" && PT_ONLY_KEYS.has(k) && !SAME_KEYS.has(k)) throw new PtKeyError(k);
887
+ if (hit === void 0 && o.strict && dir === "toPT" && PT_ONLY_KEYS.has(k) && !SAME_KEYS.has(k))
888
+ throw new PtKeyError(k);
831
889
  const outKey = hit ?? k, ptKey = dir === "toEN" ? k : outKey;
832
890
  out[outKey] = translate(v, o, own, ptKey);
833
891
  }
834
892
  return out;
835
893
  }
836
894
  if (typeof value === "string" && own !== null) {
837
- const idx = SVAL_IDX[container]?.[own] ?? VAL_IDX[own], mapped = idx?.[dir]?.[kind]?.get(value);
838
- if (mapped !== void 0) return mapped;
839
- if (o.strict && dir === "toPT" && kind === "req" && PT_ONLY_VALUES[own]?.has(value) && !EN_ALSO.has(`${own}|${value}`)) throw new PtKeyError(own, value);
840
- if (o.paths && PATH_KEYS.has(own)) return rewritePath(value, dir);
895
+ const idx = (container === null ? void 0 : SVAL_IDX[container]?.[own]) ?? VAL_IDX[own], mapped = idx?.[dir]?.[kind]?.get(value);
896
+ if (mapped !== void 0)
897
+ return mapped;
898
+ if (o.strict && dir === "toPT" && kind === "req" && PT_ONLY_VALUES[own]?.has(value) && !EN_ALSO.has(`${own}|${value}`))
899
+ throw new PtKeyError(own, value);
900
+ if (o.paths && PATH_KEYS.has(own))
901
+ return rewritePath(value, dir);
841
902
  }
842
903
  return value;
843
904
  }
@@ -873,6 +934,17 @@ var ERROR_CODES = {
873
934
  horario_passado: "time_in_past",
874
935
  sem_vagas: "no_slots",
875
936
  sem_vaga_livre: "no_free_slot",
937
+ email_em_uso: "email_in_use",
938
+ email_de_revisao: "review_account_email",
939
+ usuario_sem_senha: "user_without_password",
940
+ redefinicao_recente: "reset_recently_sent",
941
+ email_falhou: "email_send_failed",
942
+ email_nao_configurado: "email_not_configured",
943
+ conta_revisao: "review_account_restricted",
944
+ conta_revisao_expirada: "review_account_expired",
945
+ conta_revisao_revogada: "review_account_revoked",
946
+ conta_revisao_limite: "review_account_limit",
947
+ conta_revisao_inexistente: "review_account_not_found",
876
948
  post_inexistente: "post_not_found",
877
949
  destino_inexistente: "item_not_found",
878
950
  acao_inexistente: "action_not_found",
@@ -898,6 +970,10 @@ var ERROR_CODES = {
898
970
  workspace_da_plataforma: "platform_workspace",
899
971
  migracao_pendente: "migration_pending",
900
972
  chave_em_uso: "api_key_in_use",
973
+ token_invalido: "invalid_token",
974
+ pedido_inexistente: "request_not_found",
975
+ pedido_de_outro_navegador: "request_from_another_browser",
976
+ conexao_inexistente: "connection_not_found",
901
977
  video_inexistente: "video_not_found",
902
978
  sem_video: "no_video",
903
979
  sem_avatar: "no_avatar",
@@ -958,7 +1034,7 @@ var ERROR_CODES = {
958
1034
  };
959
1035
  var ERROR_CODES_REVERSE = Object.fromEntries(Object.entries(ERROR_CODES).map(([pt, en]) => [en, pt]));
960
1036
 
961
- // ../core/src/segredos.mjs
1037
+ // ../core/src/segredos.ts
962
1038
  import { execFileSync } from "node:child_process";
963
1039
  import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
964
1040
  import { homedir } from "node:os";
@@ -1044,7 +1120,7 @@ function migrar(loja = segredos, refs = REFS_CONHECIDAS) {
1044
1120
  return r;
1045
1121
  }
1046
1122
 
1047
- // ../core/src/adaptadores/remoto.mjs
1123
+ // ../core/src/adaptadores/remoto.ts
1048
1124
  var REF_CHAVE = "praeko/chave";
1049
1125
  var itemDoContrato = (i) => ({ rede: i.rede, id: i.id, estado: i.estado, quando: i.quando, ...i.url ? { url: i.url } : {}, ...i.studio ? { studio: i.studio } : {}, ...i.aviso ? { aviso: i.aviso } : {}, ...i.custom ? { custom: true } : {} });
1050
1126
  function criar({ config, ctx, praeko, arauto } = {}) {
@@ -3,18 +3,18 @@ import {
3
3
  caminhoEstado,
4
4
  caminhoLeitura,
5
5
  pastaEstado
6
- } from "./chunk-IZEX746E.mjs";
6
+ } from "./chunk-SPELUAPP.mjs";
7
7
  import {
8
8
  segredos
9
- } from "./chunk-QDZLRBTP.mjs";
9
+ } from "./chunk-FZ2EWUHA.mjs";
10
10
  import {
11
11
  validarPedido
12
- } from "./chunk-Z325UM27.mjs";
12
+ } from "./chunk-SL4UDIFM.mjs";
13
13
  import {
14
14
  __export
15
15
  } from "./chunk-LCFK6X55.mjs";
16
16
 
17
- // ../core/src/adaptadores/buffer.mjs
17
+ // ../core/src/adaptadores/buffer.ts
18
18
  var buffer_exports = {};
19
19
  __export(buffer_exports, {
20
20
  REF_CHAVE: () => REF_CHAVE,
@@ -22,7 +22,7 @@ __export(buffer_exports, {
22
22
  });
23
23
  import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "node:fs";
24
24
 
25
- // ../core/src/hospedagem.mjs
25
+ // ../core/src/hospedagem.ts
26
26
  import { randomBytes } from "node:crypto";
27
27
  import { spawnSync } from "node:child_process";
28
28
  import { basename } from "node:path";
@@ -48,7 +48,7 @@ function apagar({ h, chave, cwd, rodar = spawnSync }) {
48
48
  return r.status === 0;
49
49
  }
50
50
 
51
- // ../core/src/capa-quadro.mjs
51
+ // ../core/src/capa-quadro.ts
52
52
  var capa_quadro_exports = {};
53
53
  __export(capa_quadro_exports, {
54
54
  argsFfmpeg: () => argsFfmpeg,
@@ -89,7 +89,7 @@ function embutirCapa({ video, capa, saida, quadros = 2, rodar = spawnSync2 }) {
89
89
  return { saida, quadros };
90
90
  }
91
91
 
92
- // ../core/src/config.mjs
92
+ // ../core/src/config.ts
93
93
  import { existsSync, readFileSync } from "node:fs";
94
94
  import { dirname, join, resolve } from "node:path";
95
95
  var ARQUIVOS_CONFIG = ["praeko.json", "arauto.json", "marca.json"];
@@ -113,7 +113,7 @@ function acharConfig(de = process.cwd()) {
113
113
  }
114
114
  }
115
115
 
116
- // ../core/src/contexto.mjs
116
+ // ../core/src/contexto.ts
117
117
  function criarContexto({ config, raiz = process.cwd(), log = console.log, fetchFn = globalThis.fetch, segredos: segredos2 = segredos, agora = () => /* @__PURE__ */ new Date() } = {}) {
118
118
  const h = () => configHospedagem(config);
119
119
  return {
@@ -129,7 +129,7 @@ function criarContexto({ config, raiz = process.cwd(), log = console.log, fetchF
129
129
  };
130
130
  }
131
131
 
132
- // ../core/src/index.mjs
132
+ // ../core/src/index.ts
133
133
  var PADRAO = {
134
134
  utm: { instagram: ["instagram", "bio"], youtube: ["youtube", "short"], tiktok: ["tiktok", "bio"] },
135
135
  limites: { youtubeTitulo: 100, youtubeDescricao: 5e3, instagram: 2200, tiktok: 2200, hashtagsInstagram: 30 },
@@ -191,7 +191,7 @@ ${tags(ep.hashtags?.tiktok)}`;
191
191
  return { erros, avisos };
192
192
  }
193
193
 
194
- // ../core/src/buffer-api.mjs
194
+ // ../core/src/buffer-api.ts
195
195
  var ENDPOINT = "https://api.buffer.com";
196
196
  var ANTECEDENCIA_MIN_MS = 5 * 6e4;
197
197
  async function gql(consulta, variaveis, { chave, fetchFn = fetch } = {}) {
@@ -232,7 +232,7 @@ async function apagarPost(id, op) {
232
232
  return true;
233
233
  }
234
234
 
235
- // ../core/src/buffer.mjs
235
+ // ../core/src/buffer.ts
236
236
  var tags2 = (l) => (l || []).join(" ");
237
237
  function parseQuando(txt, { fuso = "-03:00", agora = /* @__PURE__ */ new Date() } = {}) {
238
238
  const s = String(txt).trim();
@@ -307,7 +307,7 @@ function escolherCanais({ config, canais, redes }) {
307
307
  return { canais: saida, faltam };
308
308
  }
309
309
 
310
- // ../core/src/adaptadores/buffer.mjs
310
+ // ../core/src/adaptadores/buffer.ts
311
311
  import { randomBytes as randomBytes2 } from "node:crypto";
312
312
  var REF_CHAVE = "buffer/principal";
313
313
  function criar({ config, ctx }) {
@@ -1,6 +1,6 @@
1
1
  import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
2
2
 
3
- // ../core/src/contrato.mjs
3
+ // ../core/src/contrato.ts
4
4
  var REDES = { yt: "youtube", youtube: "youtube", ig: "instagram", instagram: "instagram", tt: "tiktok", tiktok: "tiktok" };
5
5
  var REDES_VALIDAS = ["youtube", "instagram", "tiktok"];
6
6
  var MODOS = { youtube: ["privado", "agendado", "publico"], tiktok: ["rascunho", "direto"], instagram: ["agora"] };