mimi-seed 0.9.3 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -19,7 +19,7 @@ npx mimi-seed init
19
19
  | `mimi-seed init` | 프로젝트를 Mimi Seed에 연결 (PAT 발급 + 앱 자동 등록 + `.claude/mimi-seed.md` / `AGENTS.md` 주입) |
20
20
  | `mimi-seed setup` | **가진 계정을 한 번에 연결** (안내형 마법사) — 뭐가 빠졌는지 보여주고, 각 토큰을 어디서 발급받는지 알려줌 |
21
21
  | `mimi-seed status` | 연결 상태 + 등록 앱 목록 |
22
- | `mimi-seed auth` | 자격증명 개별 인증 — `login` / `appstore` / `playstore` / `bigquery` / `jenkins` / `ci` / `googleads` / `facebook` / `instagram` |
22
+ | `mimi-seed auth` | 자격증명 개별 인증 — `login` / `appstore` / `playstore` / `bigquery` / `jenkins` / `ci` / `googleads` / `facebook` / `instagram` / `threads` |
23
23
  | `mimi-seed doctor` | 환경 진단 (토큰·Git·앱·CI 한 번에 체크) |
24
24
  | `mimi-seed check` | 출시 전 Readiness 점검 (점수 + 블로커) |
25
25
  | `mimi-seed notes` | AI 릴리즈 노트 생성 (git log → 3 톤 → 다국어 → 적용) |
@@ -187,10 +187,15 @@ mimi-seed auth bigquery # BigQuery 서비스 계정 (선택)
187
187
  mimi-seed auth jenkins # Jenkins (저장 전 서버 프로브)
188
188
  mimi-seed auth ci # GitHub Actions / GitLab CI
189
189
  mimi-seed auth googleads # Google Ads
190
+ mimi-seed auth meta # Facebook · Instagram · Threads 통합 진입
190
191
  mimi-seed auth facebook # Facebook 페이지
191
192
  mimi-seed auth instagram # Instagram
193
+ mimi-seed auth threads # Threads
192
194
  ```
193
195
 
196
+ Facebook·Instagram·Threads 토큰은 저장된 만료일을 함께 확인합니다. 만료됐거나 7일 이내 만료 예정이면
197
+ `mimi-seed setup`이 자동으로 재연결 목록에 올리며, 위 개별 명령으로 바로 갱신할 수도 있습니다.
198
+
194
199
  ### login 옵션
195
200
 
196
201
  | 옵션 | 설명 |
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  catalog
4
- } from "./chunk-SZQFVTEW.js";
4
+ } from "./chunk-VZQHUWXR.js";
5
5
 
6
6
  // src/mcp-client.ts
7
7
  var M = catalog(
@@ -5,7 +5,7 @@ import {
5
5
  resolveLang,
6
6
  t,
7
7
  writeSettings
8
- } from "./chunk-SZQFVTEW.js";
8
+ } from "./chunk-VZQHUWXR.js";
9
9
 
10
10
  // src/setup.ts
11
11
  import kleur2 from "kleur";
@@ -45,6 +45,27 @@ function readJson(home, name) {
45
45
  return null;
46
46
  }
47
47
  }
48
+ var EXPIRING_SOON_MS = 7 * 24 * 60 * 60 * 1e3;
49
+ function detectSocialToken(home, file, idKey, tokenKey) {
50
+ const cfg = readJson(home, file);
51
+ const id = cfg?.[idKey];
52
+ const token = cfg?.[tokenKey];
53
+ if (typeof id !== "string" || !id || typeof token !== "string" || !token) {
54
+ return { present: false };
55
+ }
56
+ const display = typeof cfg.username === "string" && cfg.username || typeof cfg.pageName === "string" && cfg.pageName || id;
57
+ const expiresAt = typeof cfg.expiresAt === "string" ? Date.parse(cfg.expiresAt) : NaN;
58
+ if (!Number.isFinite(expiresAt)) return { present: true, detail: display };
59
+ const remainingMs = expiresAt - Date.now();
60
+ if (remainingMs <= 0) {
61
+ return { present: true, detail: display, freshness: "expired", daysRemaining: 0 };
62
+ }
63
+ const daysRemaining = Math.max(1, Math.ceil(remainingMs / 864e5));
64
+ if (remainingMs <= EXPIRING_SOON_MS) {
65
+ return { present: true, detail: display, freshness: "expiring", daysRemaining };
66
+ }
67
+ return { present: true, detail: display, freshness: "fresh", daysRemaining };
68
+ }
48
69
  function hasPlaySa(home) {
49
70
  if (hasFile(home, "play-service-account.json")) return true;
50
71
  try {
@@ -365,10 +386,7 @@ var CREDENTIALS = [
365
386
  ]
366
387
  },
367
388
  docsAnchor: "facebook",
368
- detect: (home) => {
369
- const cfg = readJson(home, "facebook.json");
370
- return cfg?.pageId ? { present: true, detail: cfg.pageName ?? cfg.pageId } : { present: false };
371
- }
389
+ detect: (home) => detectSocialToken(home, "facebook.json", "pageId", "pageAccessToken")
372
390
  },
373
391
  {
374
392
  id: "instagram",
@@ -396,10 +414,37 @@ var CREDENTIALS = [
396
414
  ]
397
415
  },
398
416
  docsAnchor: "instagram",
399
- detect: (home) => {
400
- const cfg = readJson(home, "instagram.json");
401
- return cfg?.userId ? { present: true, detail: cfg.username ?? cfg.userId } : { present: false };
402
- }
417
+ detect: (home) => detectSocialToken(home, "instagram.json", "userId", "accessToken")
418
+ },
419
+ {
420
+ id: "threads",
421
+ label: {
422
+ ko: "Threads",
423
+ en: "Threads"
424
+ },
425
+ requirement: "optional",
426
+ group: "marketing",
427
+ file: "~/.mimi-seed/threads.json",
428
+ fix: "mimi-seed auth threads",
429
+ setup: { kind: "mcp-bin", bin: "mimi-seed-social-auth", args: ["threads"] },
430
+ obtain: {
431
+ ko: [
432
+ "Instagram \uACFC **\uBCC4\uAC1C \uACC4\uC815\xB7\uBCC4\uAC1C \uD1A0\uD070**\uC774\uB2E4 (Threads Graph API).",
433
+ "developers.facebook.com \u2192 \uC571 \u2192 Threads API use case \uCD94\uAC00",
434
+ "\u2192 \uAD8C\uD55C threads_basic, threads_content_publish",
435
+ "\u2192 Threads \uB85C\uADF8\uC778\uC73C\uB85C authorize \u2192 long-lived \uD1A0\uD070 \uAD50\uD658 (\uC57D 60\uC77C).",
436
+ "userId \uB294 \uD1A0\uD070\uC5D0\uC11C \uC790\uB3D9 \uC870\uD68C\uB41C\uB2E4."
437
+ ],
438
+ en: [
439
+ "A **separate account and token** from Instagram (Threads Graph API).",
440
+ "developers.facebook.com \u2192 your app \u2192 add the Threads API use case",
441
+ "\u2192 permissions threads_basic, threads_content_publish",
442
+ "\u2192 authorize with Threads login \u2192 exchange for a long-lived token (~60 days).",
443
+ "userId is looked up from the token."
444
+ ]
445
+ },
446
+ docsAnchor: "threads",
447
+ detect: (home) => detectSocialToken(home, "threads.json", "userId", "accessToken")
403
448
  },
404
449
  {
405
450
  id: "anthropic",
@@ -457,7 +502,9 @@ function planSetup(detected, opts = {}) {
457
502
  return CREDENTIALS.filter((spec) => {
458
503
  if (only && !only.has(spec.id)) return false;
459
504
  if (reconnect.has(spec.id)) return true;
460
- if (detected.get(spec.id)?.present) return false;
505
+ const found = detected.get(spec.id);
506
+ if (found?.freshness === "expired" || found?.freshness === "expiring") return true;
507
+ if (found?.present) return false;
461
508
  if (spec.requirement === "platform" && platforms.length > 0 && !platforms.includes(spec.platform)) {
462
509
  return false;
463
510
  }
@@ -1384,7 +1431,7 @@ async function cmdDeploy(argv) {
1384
1431
  }
1385
1432
  let appId = args.appId;
1386
1433
  if (!appId) {
1387
- const { mcpCall } = await import("./mcp-client-PVWDCYJN.js");
1434
+ const { mcpCall } = await import("./mcp-client-DQBKWHT4.js");
1388
1435
  const r = await mcpCall(cfg.endpoint, cfg.token, "list_apps", {});
1389
1436
  if (!r.isError) {
1390
1437
  try {
@@ -1475,11 +1522,13 @@ function statusLine(spec, detected, platforms) {
1475
1522
  const d = detected.get(spec.id);
1476
1523
  const relevant = spec.requirement !== "platform" || platforms.length === 0 || platforms.includes(spec.platform);
1477
1524
  let mark;
1478
- if (d.present) mark = kleur2.green("\u2713");
1525
+ if (d.freshness === "expired") mark = kleur2.red("\u2717");
1526
+ else if (d.freshness === "expiring") mark = kleur2.yellow("!");
1527
+ else if (d.present) mark = kleur2.green("\u2713");
1479
1528
  else if (isSatisfied(spec, detected)) mark = kleur2.yellow("~");
1480
1529
  else if (spec.requirement === "optional" || !relevant) mark = kleur2.dim("\xB7");
1481
1530
  else mark = kleur2.red("\u2717");
1482
- const tail = d.present ? kleur2.dim(d.detail ?? "") : isSatisfied(spec, detected) ? kleur2.dim(`${credNote(spec) ?? t().setup.fallbackWorking}`) : kleur2.dim(`\u2192 ${spec.fix}`);
1531
+ const tail = d.freshness === "expired" ? kleur2.red(`${t().setup.tokenExpired} \u2192 ${spec.fix}`) : d.freshness === "expiring" ? kleur2.yellow(`${t().setup.tokenExpiring(d.daysRemaining ?? 0)} \u2192 ${spec.fix}`) : d.present ? kleur2.dim(d.detail ?? "") : isSatisfied(spec, detected) ? kleur2.dim(`${credNote(spec) ?? t().setup.fallbackWorking}`) : kleur2.dim(`\u2192 ${spec.fix}`);
1483
1532
  return ` ${mark} ${credLabel(spec).padEnd(24)} ${tail}`;
1484
1533
  }
1485
1534
  function printStatus(detected, platforms) {
@@ -78,6 +78,8 @@ var ko = {
78
78
  runInTerminal: " \uB300\uD654\uD615\uC73C\uB85C \uC5F0\uACB0\uD558\uB824\uBA74 \uD130\uBBF8\uB110\uC5D0\uC11C: mimi-seed setup",
79
79
  onlyAlreadyDone: " \u2705 \uC694\uCCAD\uD55C \uD56D\uBAA9\uC740 \uC774\uBBF8 \uC5F0\uACB0\uB3FC \uC788\uC5B4.",
80
80
  onlyReconnectHint: " \uB2E4\uC2DC \uC124\uC815\uD558\uB824\uBA74: mimi-seed setup --reconnect <id>",
81
+ tokenExpired: "\uD1A0\uD070 \uB9CC\uB8CC \u2014 \uB2E4\uC2DC \uC5F0\uACB0 \uD544\uC694",
82
+ tokenExpiring: (days) => `\uD1A0\uD070 ${days}\uC77C \uD6C4 \uB9CC\uB8CC \u2014 \uC9C0\uAE08 \uAC31\uC2E0 \uAD8C\uC7A5`,
81
83
  allDone: " \u2705 \uC5F0\uACB0\uD560 \uAC8C \uB354 \uC5C6\uC5B4. \uB2E4 \uB410\uB2E4.",
82
84
  planCount: (n) => ` ${n}\uAC1C \uD56D\uBAA9\uC744 \uC21C\uC11C\uB300\uB85C \uBB3C\uC5B4\uBCFC\uAC8C. \uC5B8\uC81C\uB4E0 s=\uAC74\uB108\uB6F0\uAE30, q=\uC885\uB8CC.`,
83
85
  prompt: " [c] \uC5F0\uACB0 [s] \uAC74\uB108\uB6F0\uAE30 [?] \uC774\uAC74 \uC5B4\uB5BB\uAC8C \uAD6C\uD558\uB098\uC694 [q] \uC885\uB8CC : ",
@@ -177,6 +179,8 @@ MIMI_SEED_LANG takes precedence when set.`,
177
179
  runInTerminal: " To connect interactively, run: mimi-seed setup",
178
180
  onlyAlreadyDone: " \u2705 What you asked for is already connected.",
179
181
  onlyReconnectHint: " To redo it: mimi-seed setup --reconnect <id>",
182
+ tokenExpired: "token expired \u2014 reconnect required",
183
+ tokenExpiring: (days) => `token expires in ${days} day(s) \u2014 refresh now`,
180
184
  allDone: " \u2705 Nothing left to connect. You're set.",
181
185
  planCount: (n) => ` I'll walk you through ${n} item(s). s = skip, q = quit, anytime.`,
182
186
  prompt: " [c] connect [s] skip [?] how do I get this [q] quit : ",
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  mcpCall
4
- } from "./chunk-QU2IDWJ2.js";
4
+ } from "./chunk-HCCOWX3G.js";
5
5
  import {
6
6
  CONFIG_LOCATION,
7
7
  CREDENTIALS,
@@ -20,14 +20,14 @@ import {
20
20
  runMcpBin,
21
21
  tryCredById,
22
22
  writeConfig
23
- } from "./chunk-BXN3EMF6.js";
23
+ } from "./chunk-PL7N2FSO.js";
24
24
  import {
25
25
  catalog,
26
26
  isLang,
27
27
  resolveLang,
28
28
  t,
29
29
  writeSettings
30
- } from "./chunk-SZQFVTEW.js";
30
+ } from "./chunk-VZQHUWXR.js";
31
31
 
32
32
  // src/index.ts
33
33
  import os3 from "os";
@@ -983,11 +983,13 @@ ${kleur5.bold("\uD50C\uB7AB\uD3FC\uBCC4 \uC790\uACA9\uC99D\uBA85:")}
983
983
  ${kleur5.cyan("mimi-seed auth bigquery")} BigQuery \uC11C\uBE44\uC2A4 \uACC4\uC815 (Crashlytics export \uB4F1)
984
984
 
985
985
  ${kleur5.bold("\uBE4C\uB4DC / \uB9C8\uCF00\uD305:")}
986
+ ${kleur5.cyan("mimi-seed auth meta")} Facebook \xB7 Instagram \xB7 Threads \uD55C \uBC88\uC5D0
986
987
  ${kleur5.cyan("mimi-seed auth jenkins")} Jenkins \uC5F0\uACB0 (jenkins.json)
987
988
  ${kleur5.cyan("mimi-seed auth ci")} GitHub Actions / GitLab CI (ci.json)
988
989
  ${kleur5.cyan("mimi-seed auth googleads")} Google Ads (google-ads.json)
989
990
  ${kleur5.cyan("mimi-seed auth facebook")} Facebook \uD398\uC774\uC9C0
990
991
  ${kleur5.cyan("mimi-seed auth instagram")} Instagram
992
+ ${kleur5.cyan("mimi-seed auth threads")} Threads
991
993
 
992
994
  ${kleur5.bold("\uC804\uCCB4 \uC0C1\uD0DC:")}
993
995
  ${kleur5.cyan("mimi-seed auth status --all")} \uBAA8\uB4E0 \uC790\uACA9\uC99D\uBA85 \uBCF4\uC720 \uC5EC\uBD80 \uD55C\uB208\uC5D0
@@ -1018,11 +1020,13 @@ ${kleur5.bold("Per-platform credentials:")}
1018
1020
  ${kleur5.cyan("mimi-seed auth bigquery")} BigQuery service account (Crashlytics export, etc.)
1019
1021
 
1020
1022
  ${kleur5.bold("Build / marketing:")}
1023
+ ${kleur5.cyan("mimi-seed auth meta")} Facebook \xB7 Instagram \xB7 Threads in one flow
1021
1024
  ${kleur5.cyan("mimi-seed auth jenkins")} Jenkins connection (jenkins.json)
1022
1025
  ${kleur5.cyan("mimi-seed auth ci")} GitHub Actions / GitLab CI (ci.json)
1023
1026
  ${kleur5.cyan("mimi-seed auth googleads")} Google Ads (google-ads.json)
1024
1027
  ${kleur5.cyan("mimi-seed auth facebook")} Facebook Page
1025
1028
  ${kleur5.cyan("mimi-seed auth instagram")} Instagram
1029
+ ${kleur5.cyan("mimi-seed auth threads")} Threads
1026
1030
 
1027
1031
  ${kleur5.bold("Everything at a glance:")}
1028
1032
  ${kleur5.cyan("mimi-seed auth status --all")} which credentials you have
@@ -1047,8 +1051,8 @@ function printAllCredStatus() {
1047
1051
  const detected = detectAll();
1048
1052
  for (const spec of CREDENTIALS) {
1049
1053
  const d = detected.get(spec.id);
1050
- const mark = d.present ? kleur5.green("\u2713") : isSatisfied(spec, detected) ? kleur5.yellow("~") : spec.requirement === "optional" ? kleur5.dim("\xB7") : kleur5.red("\u2717");
1051
- const tail = d.present ? kleur5.dim(d.detail ?? "") : kleur5.dim(`\u2192 ${spec.fix}`);
1054
+ const mark = d.freshness === "expired" ? kleur5.red("\u2717") : d.freshness === "expiring" ? kleur5.yellow("!") : d.present ? kleur5.green("\u2713") : isSatisfied(spec, detected) ? kleur5.yellow("~") : spec.requirement === "optional" ? kleur5.dim("\xB7") : kleur5.red("\u2717");
1055
+ const tail = d.freshness === "expired" ? kleur5.red(`${t().setup.tokenExpired} \u2192 ${spec.fix}`) : d.freshness === "expiring" ? kleur5.yellow(`${t().setup.tokenExpiring(d.daysRemaining ?? 0)} \u2192 ${spec.fix}`) : d.present ? kleur5.dim(d.detail ?? "") : kleur5.dim(`\u2192 ${spec.fix}`);
1052
1056
  log(` ${mark} ${credLabel(spec).padEnd(24)} ${tail}`);
1053
1057
  }
1054
1058
  log(kleur5.dim(M5().connectAll));
@@ -1069,10 +1073,12 @@ async function cmdAuth(args) {
1069
1073
  if (sub === "bigquery") return void exitWith(await runMcpBin("mimi-seed-bigquery-auth", rest));
1070
1074
  if (sub === "jenkins") return void exitWith(await runMcpBin("mimi-seed-jenkins-auth", rest));
1071
1075
  if (sub === "googleads") return void exitWith(await runMcpBin("mimi-seed-googleads-auth", rest));
1076
+ if (sub === "meta") return void exitWith(await runMcpBin("mimi-seed-social-auth", ["all"]));
1072
1077
  if (sub === "facebook") return void exitWith(await runMcpBin("mimi-seed-social-auth", ["facebook"]));
1073
1078
  if (sub === "instagram") return void exitWith(await runMcpBin("mimi-seed-social-auth", ["instagram"]));
1079
+ if (sub === "threads") return void exitWith(await runMcpBin("mimi-seed-social-auth", ["threads"]));
1074
1080
  if (sub === "ci") {
1075
- const { cmdSetup: cmdSetup2 } = await import("./setup-3XTSGFEF.js");
1081
+ const { cmdSetup: cmdSetup2 } = await import("./setup-EQVJO6F7.js");
1076
1082
  await cmdSetup2(["--only", "github,gitlab", "--reconnect", "github,gitlab"]);
1077
1083
  return;
1078
1084
  }
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  mcpCall
4
- } from "./chunk-QU2IDWJ2.js";
5
- import "./chunk-SZQFVTEW.js";
4
+ } from "./chunk-HCCOWX3G.js";
5
+ import "./chunk-VZQHUWXR.js";
6
6
  export {
7
7
  mcpCall
8
8
  };
@@ -3,8 +3,8 @@ import {
3
3
  cmdSetup,
4
4
  parseSetupArgs,
5
5
  resolveMode
6
- } from "./chunk-BXN3EMF6.js";
7
- import "./chunk-SZQFVTEW.js";
6
+ } from "./chunk-PL7N2FSO.js";
7
+ import "./chunk-VZQHUWXR.js";
8
8
  export {
9
9
  cmdSetup,
10
10
  parseSetupArgs,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mimi-seed",
3
- "version": "0.9.3",
3
+ "version": "0.10.0",
4
4
  "description": "Mimi Seed CLI — Claude Code와 Codex에서 앱 출시 운영을 관리합니다.",
5
5
  "bin": {
6
6
  "mimi-seed": "dist/index.js"