@julioventura/opensquad 0.1.17

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 (247) hide show
  1. package/README.md +433 -0
  2. package/_opensquad/config/playwright.config.json +11 -0
  3. package/_opensquad/core/architect.agent.yaml +112 -0
  4. package/_opensquad/core/best-practices/_catalog.yaml +126 -0
  5. package/_opensquad/core/best-practices/blog-post.md +132 -0
  6. package/_opensquad/core/best-practices/blog-seo.md +127 -0
  7. package/_opensquad/core/best-practices/brand-resolution-checklist.md +172 -0
  8. package/_opensquad/core/best-practices/copywriting.md +441 -0
  9. package/_opensquad/core/best-practices/data-analysis.md +401 -0
  10. package/_opensquad/core/best-practices/email-newsletter.md +118 -0
  11. package/_opensquad/core/best-practices/email-sales.md +110 -0
  12. package/_opensquad/core/best-practices/image-design.md +348 -0
  13. package/_opensquad/core/best-practices/instagram-feed.md +235 -0
  14. package/_opensquad/core/best-practices/instagram-reels.md +112 -0
  15. package/_opensquad/core/best-practices/instagram-stories.md +107 -0
  16. package/_opensquad/core/best-practices/linkedin-article.md +116 -0
  17. package/_opensquad/core/best-practices/linkedin-post.md +121 -0
  18. package/_opensquad/core/best-practices/researching.md +349 -0
  19. package/_opensquad/core/best-practices/review.md +269 -0
  20. package/_opensquad/core/best-practices/run-recovery.md +61 -0
  21. package/_opensquad/core/best-practices/social-networks-publishing.md +327 -0
  22. package/_opensquad/core/best-practices/squad-creation-checklist.md +32 -0
  23. package/_opensquad/core/best-practices/strategist.md +344 -0
  24. package/_opensquad/core/best-practices/technical-writing.md +365 -0
  25. package/_opensquad/core/best-practices/twitter-post.md +105 -0
  26. package/_opensquad/core/best-practices/twitter-thread.md +122 -0
  27. package/_opensquad/core/best-practices/whatsapp-broadcast.md +107 -0
  28. package/_opensquad/core/best-practices/youtube-script.md +122 -0
  29. package/_opensquad/core/best-practices/youtube-shorts.md +112 -0
  30. package/_opensquad/core/defaults/youtube-video-assembly.json +84 -0
  31. package/_opensquad/core/prompts/build.prompt.md +613 -0
  32. package/_opensquad/core/prompts/design.prompt.md +606 -0
  33. package/_opensquad/core/prompts/discovery.prompt.md +377 -0
  34. package/_opensquad/core/prompts/sherlock-instagram.md +123 -0
  35. package/_opensquad/core/prompts/sherlock-linkedin.md +73 -0
  36. package/_opensquad/core/prompts/sherlock-shared.md +684 -0
  37. package/_opensquad/core/prompts/sherlock-twitter.md +78 -0
  38. package/_opensquad/core/prompts/sherlock-youtube.md +85 -0
  39. package/_opensquad/core/runner.pipeline.md +743 -0
  40. package/_opensquad/core/skills.engine.md +384 -0
  41. package/bin/opensquad.js +108 -0
  42. package/dashboard/index.html +15 -0
  43. package/dashboard/package-lock.json +1964 -0
  44. package/dashboard/package.json +28 -0
  45. package/dashboard/public/assets/avatars/Female1_1wave.png +0 -0
  46. package/dashboard/public/assets/avatars/Female1_2wave.png +0 -0
  47. package/dashboard/public/assets/avatars/Female1_blink.png +0 -0
  48. package/dashboard/public/assets/avatars/Female1_talk.png +0 -0
  49. package/dashboard/public/assets/avatars/Female2_1wave.png +0 -0
  50. package/dashboard/public/assets/avatars/Female2_2wave.png +0 -0
  51. package/dashboard/public/assets/avatars/Female2_blink.png +0 -0
  52. package/dashboard/public/assets/avatars/Female2_talk.png +0 -0
  53. package/dashboard/public/assets/avatars/Female3_blink.png +0 -0
  54. package/dashboard/public/assets/avatars/Female3_talk.png +0 -0
  55. package/dashboard/public/assets/avatars/Female3_wave.png +0 -0
  56. package/dashboard/public/assets/avatars/Female4_blink.png +0 -0
  57. package/dashboard/public/assets/avatars/Female4_talk.png +0 -0
  58. package/dashboard/public/assets/avatars/Female4_wave.png +0 -0
  59. package/dashboard/public/assets/avatars/Female5_blink.png +0 -0
  60. package/dashboard/public/assets/avatars/Female5_talk.png +0 -0
  61. package/dashboard/public/assets/avatars/Female5_wave.png +0 -0
  62. package/dashboard/public/assets/avatars/Female6_blink.png +0 -0
  63. package/dashboard/public/assets/avatars/Female6_talk.png +0 -0
  64. package/dashboard/public/assets/avatars/Female6_wave.png +0 -0
  65. package/dashboard/public/assets/avatars/Male1_1wave.png +0 -0
  66. package/dashboard/public/assets/avatars/Male1_2wave.png +0 -0
  67. package/dashboard/public/assets/avatars/Male1_blink.png +0 -0
  68. package/dashboard/public/assets/avatars/Male1_talk.png +0 -0
  69. package/dashboard/public/assets/avatars/Male2_1wave.png +0 -0
  70. package/dashboard/public/assets/avatars/Male2_2wave.png +0 -0
  71. package/dashboard/public/assets/avatars/Male2_blink.png +0 -0
  72. package/dashboard/public/assets/avatars/Male2_talk.png +0 -0
  73. package/dashboard/public/assets/avatars/Male3_blink.png +0 -0
  74. package/dashboard/public/assets/avatars/Male3_talk.png +0 -0
  75. package/dashboard/public/assets/avatars/Male3_wave.png +0 -0
  76. package/dashboard/public/assets/avatars/Male4_blink.png +0 -0
  77. package/dashboard/public/assets/avatars/Male4_talk.png +0 -0
  78. package/dashboard/public/assets/avatars/Male4_wave.png +0 -0
  79. package/dashboard/public/assets/desks/desktop_set_black_down.png +0 -0
  80. package/dashboard/public/assets/desks/desktop_set_black_down_coding-1.png +0 -0
  81. package/dashboard/public/assets/desks/desktop_set_black_down_coding.png +0 -0
  82. package/dashboard/public/assets/desks/desktop_set_black_up.png +0 -0
  83. package/dashboard/public/assets/desks/desktop_set_white_down.png +0 -0
  84. package/dashboard/public/assets/desks/desktop_set_white_down_coding-1.png +0 -0
  85. package/dashboard/public/assets/desks/desktop_set_white_down_coding.png +0 -0
  86. package/dashboard/public/assets/desks/desktop_set_white_up.png +0 -0
  87. package/dashboard/public/assets/furniture/armchair_tan.png +0 -0
  88. package/dashboard/public/assets/furniture/armchair_tan_down.png +0 -0
  89. package/dashboard/public/assets/furniture/backpack_blue.png +0 -0
  90. package/dashboard/public/assets/furniture/backpack_red.png +0 -0
  91. package/dashboard/public/assets/furniture/blinds.png +0 -0
  92. package/dashboard/public/assets/furniture/blinds_large_closed_white.png +0 -0
  93. package/dashboard/public/assets/furniture/bookshelf.png +0 -0
  94. package/dashboard/public/assets/furniture/bookshelf_purple_tall.png +0 -0
  95. package/dashboard/public/assets/furniture/bulletin_board.png +0 -0
  96. package/dashboard/public/assets/furniture/clock.png +0 -0
  97. package/dashboard/public/assets/furniture/coffee_mug.png +0 -0
  98. package/dashboard/public/assets/furniture/coffee_mug_blue.png +0 -0
  99. package/dashboard/public/assets/furniture/coffee_table.png +0 -0
  100. package/dashboard/public/assets/furniture/coffeepot_right.png +0 -0
  101. package/dashboard/public/assets/furniture/coffeetable_black_horizontal.png +0 -0
  102. package/dashboard/public/assets/furniture/couch.png +0 -0
  103. package/dashboard/public/assets/furniture/couch_tan_down.png +0 -0
  104. package/dashboard/public/assets/furniture/cushion_blue.png +0 -0
  105. package/dashboard/public/assets/furniture/cushion_tan.png +0 -0
  106. package/dashboard/public/assets/furniture/desk_wood.png +0 -0
  107. package/dashboard/public/assets/furniture/fancy_rug.png +0 -0
  108. package/dashboard/public/assets/furniture/fancy_rug_wide.png +0 -0
  109. package/dashboard/public/assets/furniture/flowers1.png +0 -0
  110. package/dashboard/public/assets/furniture/flowers2.png +0 -0
  111. package/dashboard/public/assets/furniture/lamp_tan.png +0 -0
  112. package/dashboard/public/assets/furniture/lantern.png +0 -0
  113. package/dashboard/public/assets/furniture/monstera.png +0 -0
  114. package/dashboard/public/assets/furniture/monstera_small.png +0 -0
  115. package/dashboard/public/assets/furniture/picture_frame.png +0 -0
  116. package/dashboard/public/assets/furniture/plant1.png +0 -0
  117. package/dashboard/public/assets/furniture/plant2.png +0 -0
  118. package/dashboard/public/assets/furniture/plant3.png +0 -0
  119. package/dashboard/public/assets/furniture/plant_poof.png +0 -0
  120. package/dashboard/public/assets/furniture/plant_spindly.png +0 -0
  121. package/dashboard/public/assets/furniture/poster_blue.png +0 -0
  122. package/dashboard/public/assets/furniture/rug.png +0 -0
  123. package/dashboard/public/assets/furniture/succulent_blue.png +0 -0
  124. package/dashboard/public/assets/furniture/succulent_green.png +0 -0
  125. package/dashboard/public/assets/furniture/treasurechest_closed_gold.png +0 -0
  126. package/dashboard/public/assets/furniture/water_cooler_better.png +0 -0
  127. package/dashboard/public/assets/furniture/whiteboard.png +0 -0
  128. package/dashboard/public/assets/furniture/whiteboard_stand_graph.png +0 -0
  129. package/dashboard/public/assets/furniture/window_blinds_open.png +0 -0
  130. package/dashboard/src/App.tsx +46 -0
  131. package/dashboard/src/components/RunDashboardButton.tsx +92 -0
  132. package/dashboard/src/components/SquadCard.tsx +49 -0
  133. package/dashboard/src/components/SquadSelector.tsx +67 -0
  134. package/dashboard/src/components/StatusBadge.tsx +32 -0
  135. package/dashboard/src/components/StatusBar.tsx +116 -0
  136. package/dashboard/src/hooks/useSquadSocket.ts +135 -0
  137. package/dashboard/src/lib/formatTime.ts +16 -0
  138. package/dashboard/src/lib/normalizeState.ts +25 -0
  139. package/dashboard/src/main.tsx +10 -0
  140. package/dashboard/src/office/AgentSprite.ts +241 -0
  141. package/dashboard/src/office/OfficeScene.ts +153 -0
  142. package/dashboard/src/office/PhaserGame.tsx +80 -0
  143. package/dashboard/src/office/RoomBuilder.ts +190 -0
  144. package/dashboard/src/office/assetKeys.ts +150 -0
  145. package/dashboard/src/office/palette.ts +32 -0
  146. package/dashboard/src/plugin/squadWatcher.ts +397 -0
  147. package/dashboard/src/store/useSquadStore.ts +56 -0
  148. package/dashboard/src/styles/globals.css +36 -0
  149. package/dashboard/src/types/state.ts +63 -0
  150. package/dashboard/src/vite-env.d.ts +1 -0
  151. package/dashboard/tsconfig.json +24 -0
  152. package/dashboard/vite.config.ts +13 -0
  153. package/package.json +59 -0
  154. package/public/sfx/slide-transition-sfx.mp3 +0 -0
  155. package/skills/README.md +84 -0
  156. package/skills/apify/SKILL.md +55 -0
  157. package/skills/blotato/SKILL.md +63 -0
  158. package/skills/canva/SKILL.md +60 -0
  159. package/skills/higgsfield/SKILL.md +147 -0
  160. package/skills/image-ai-generator/SKILL.md +124 -0
  161. package/skills/image-ai-generator/scripts/generate.py +175 -0
  162. package/skills/image-creator/SKILL.md +166 -0
  163. package/skills/image-creator/editorial-slide-template.js +645 -0
  164. package/skills/image-fetcher/SKILL.md +91 -0
  165. package/skills/imgbb-uploader/SKILL.md +73 -0
  166. package/skills/imgbb-uploader/scripts/upload.js +125 -0
  167. package/skills/instagram-publisher/README.md +36 -0
  168. package/skills/instagram-publisher/SKILL.md +231 -0
  169. package/skills/instagram-publisher/scripts/publish-playwright.js +418 -0
  170. package/skills/instagram-publisher/scripts/publish.js +521 -0
  171. package/skills/opensquad-agent-creator/SKILL.md +192 -0
  172. package/skills/opensquad-skill-creator/SKILL.md +420 -0
  173. package/skills/opensquad-skill-creator/agents/analyzer.md +274 -0
  174. package/skills/opensquad-skill-creator/agents/comparator.md +202 -0
  175. package/skills/opensquad-skill-creator/agents/grader.md +223 -0
  176. package/skills/opensquad-skill-creator/assets/eval_review.html +146 -0
  177. package/skills/opensquad-skill-creator/eval-viewer/generate_review.py +471 -0
  178. package/skills/opensquad-skill-creator/eval-viewer/viewer.html +1325 -0
  179. package/skills/opensquad-skill-creator/references/schemas.md +430 -0
  180. package/skills/opensquad-skill-creator/references/skill-format.md +235 -0
  181. package/skills/opensquad-skill-creator/scripts/__init__.py +0 -0
  182. package/skills/opensquad-skill-creator/scripts/aggregate_benchmark.py +401 -0
  183. package/skills/opensquad-skill-creator/scripts/quick_validate.py +103 -0
  184. package/skills/opensquad-skill-creator/scripts/run_eval.py +310 -0
  185. package/skills/opensquad-skill-creator/scripts/utils.py +47 -0
  186. package/skills/pdf-extractor/SKILL.md +57 -0
  187. package/skills/pdf-extractor/scripts/extract.py +82 -0
  188. package/skills/resend/SKILL.md +80 -0
  189. package/skills/run-dashboard/README.md +93 -0
  190. package/skills/run-dashboard/SKILL.md +173 -0
  191. package/skills/run-dashboard/scripts/finalize-state.js +273 -0
  192. package/skills/run-dashboard/scripts/generate.js +1296 -0
  193. package/skills/run-dashboard/scripts/serve.js +135 -0
  194. package/skills/run-dashboard/templates/run-dashboard-simple.template.html +191 -0
  195. package/skills/run-dashboard/templates/run-dashboard.template.html +1164 -0
  196. package/skills/smtp-sender/SKILL.md +88 -0
  197. package/skills/smtp-sender/scripts/send.js +478 -0
  198. package/skills/template-designer/SKILL.md +201 -0
  199. package/skills/template-designer/base-templates/model-a.html +27 -0
  200. package/skills/template-designer/base-templates/model-b.html +31 -0
  201. package/skills/template-designer/base-templates/model-c.html +42 -0
  202. package/skills/youtube-publisher/SKILL.md +232 -0
  203. package/skills/youtube-publisher/scripts/publish.js +2078 -0
  204. package/src/agents-cli.js +158 -0
  205. package/src/agents.js +134 -0
  206. package/src/i18n.js +48 -0
  207. package/src/init.js +442 -0
  208. package/src/locales/en.json +79 -0
  209. package/src/locales/es.json +78 -0
  210. package/src/locales/pt-BR.json +78 -0
  211. package/src/logger.js +38 -0
  212. package/src/prompt.js +46 -0
  213. package/src/readme/README.md +146 -0
  214. package/src/runs.js +318 -0
  215. package/src/skills-cli.js +157 -0
  216. package/src/skills.js +146 -0
  217. package/src/supabase-cli.js +584 -0
  218. package/src/update.js +169 -0
  219. package/templates/_opensquad/.opensquad-version +1 -0
  220. package/templates/_opensquad/_investigations/.gitkeep +0 -0
  221. package/templates/ide-templates/antigravity/.agent/rules/opensquad.md +68 -0
  222. package/templates/ide-templates/antigravity/.agent/workflows/opensquad.md +102 -0
  223. package/templates/ide-templates/claude-code/.claude/skills/opensquad/SKILL.md +182 -0
  224. package/templates/ide-templates/claude-code/.mcp.json +8 -0
  225. package/templates/ide-templates/claude-code/CLAUDE.md +57 -0
  226. package/templates/ide-templates/codex/.agents/skills/opensquad/SKILL.md +6 -0
  227. package/templates/ide-templates/codex/AGENTS.md +120 -0
  228. package/templates/ide-templates/cursor/.cursor/commands/opensquad.md +9 -0
  229. package/templates/ide-templates/cursor/.cursor/mcp.json +8 -0
  230. package/templates/ide-templates/cursor/.cursor/rules/opensquad.mdc +62 -0
  231. package/templates/ide-templates/cursor/.cursorignore +3 -0
  232. package/templates/ide-templates/gemini-cli/.gemini/settings.json +8 -0
  233. package/templates/ide-templates/gemini-cli/.gemini/skills/opensquad/SKILL.md +186 -0
  234. package/templates/ide-templates/gemini-cli/GEMINI.md +57 -0
  235. package/templates/ide-templates/opencode/.opencode/commands/opensquad.md +9 -0
  236. package/templates/ide-templates/opencode/AGENTS.md +120 -0
  237. package/templates/ide-templates/qwen-code/.qwen/settings.json +8 -0
  238. package/templates/ide-templates/qwen-code/.qwen/skills/opensquad/SKILL.md +182 -0
  239. package/templates/ide-templates/qwen-code/QWEN.md +57 -0
  240. package/templates/ide-templates/trae/.trae/mcp.json +8 -0
  241. package/templates/ide-templates/trae/.trae/rules/opensquad.md +64 -0
  242. package/templates/ide-templates/vscode-copilot/.github/copilot-instructions.md +59 -0
  243. package/templates/ide-templates/vscode-copilot/.github/prompts/opensquad.prompt.md +209 -0
  244. package/templates/ide-templates/vscode-copilot/.vscode/mcp.json +8 -0
  245. package/templates/ide-templates/vscode-copilot/.vscode/settings.json +3 -0
  246. package/templates/package.json +8 -0
  247. package/templates/squads/.gitkeep +0 -0
@@ -0,0 +1,418 @@
1
+ #!/usr/bin/env node
2
+ import { chromium } from 'playwright';
3
+ import { resolve } from 'path';
4
+
5
+ function parseArgs(argv) {
6
+ const args = {
7
+ images: [],
8
+ caption: '',
9
+ username: '',
10
+ password: '',
11
+ profileDir: '_opensquad/_browser_profile'
12
+ };
13
+ for (let i = 2; i < argv.length; i++) {
14
+ if (argv[i] === '--images') {
15
+ if (i + 1 < argv.length) args.images = argv[++i].split(',').map(s => s.trim());
16
+ } else if (argv[i] === '--caption') {
17
+ if (i + 1 < argv.length) args.caption = argv[++i];
18
+ } else if (argv[i] === '--username') {
19
+ if (i + 1 < argv.length) args.username = argv[++i];
20
+ } else if (argv[i] === '--password') {
21
+ if (i + 1 < argv.length) args.password = argv[++i];
22
+ } else if (argv[i] === '--profile-dir') {
23
+ if (i + 1 < argv.length) args.profileDir = argv[++i];
24
+ }
25
+ }
26
+ return args;
27
+ }
28
+
29
+ const args = parseArgs(process.argv);
30
+
31
+ if (!args.images.length) {
32
+ console.error('Error: --images is required');
33
+ process.exit(1);
34
+ }
35
+ if (!args.caption) {
36
+ console.error('Error: --caption is required');
37
+ process.exit(1);
38
+ }
39
+
40
+ const resolvedImages = args.images.map(p => resolve(p));
41
+ const username = args.username || process.env.ODONTOLOGIA_INSTAGRAM_USER || 'atendimento@dentistas.com.br';
42
+ const password = args.password || process.env.ODONTOLOGIA_INSTAGRAM_PASS || 'Instagram@julio@2026';
43
+ const profileHandle = String(process.env.MUSICPLAY_INSTAGRAM_HANDLE || process.env.INSTAGRAM_PROFILE_HANDLE || 'musicplay.club')
44
+ .trim()
45
+ .replace(/^@/, '')
46
+ .replace(/^https?:\/\/www\.instagram\.com\//i, '')
47
+ .replace(/\/?$/, '');
48
+
49
+ function isInstagramSecurityUrl(url) {
50
+ return /auth_platform|recaptcha|challenge|accounts\/onetap/i.test(String(url || ''));
51
+ }
52
+
53
+ async function waitForInstagramSecurityResolution(page, reason, timeoutMs = 300000) {
54
+ if (!isInstagramSecurityUrl(page.url())) {
55
+ return false;
56
+ }
57
+
58
+ console.log(`\n⚠️ Instagram acionou uma verificação de segurança durante: ${reason}`);
59
+ console.log(`URL atual: ${page.url()}`);
60
+ console.log('👉 Resolva manualmente o captcha/verificação nessa janela.');
61
+ console.log('Vou esperar até 5 minutos a volta para o Instagram normal antes de continuar.\n');
62
+
63
+ await page.waitForFunction(
64
+ () => !/auth_platform|recaptcha|challenge|accounts\/onetap/i.test(window.location.href),
65
+ undefined,
66
+ { timeout: timeoutMs }
67
+ );
68
+
69
+ await page.waitForLoadState('networkidle', { timeout: 30000 }).catch(() => {});
70
+ await page.waitForTimeout(3000);
71
+ console.log(`✅ Verificação concluída. URL atual: ${page.url()}`);
72
+ return true;
73
+ }
74
+
75
+ async function assertExpectedInstagramOwnerSession(page, expectedHandle) {
76
+ const expectedProfileUrl = `https://www.instagram.com/${expectedHandle}/`;
77
+ await page.goto(expectedProfileUrl, { waitUntil: 'networkidle' }).catch(() => {});
78
+ await page.waitForTimeout(3000);
79
+
80
+ const ownerIndicators = [
81
+ 'button:has-text("Editar perfil")',
82
+ 'button:has-text("Edit profile")',
83
+ 'a:has-text("Editar perfil")',
84
+ 'a:has-text("Edit profile")'
85
+ ];
86
+
87
+ for (const selector of ownerIndicators) {
88
+ try {
89
+ if (await page.locator(selector).first().isVisible({ timeout: 1000 })) {
90
+ console.log(`✅ Sessão confirmada como dona de @${expectedHandle}.`);
91
+ return;
92
+ }
93
+ } catch (err) {}
94
+ }
95
+
96
+ throw new Error(
97
+ `A sessão persistente do Instagram não está autenticada como @${expectedHandle}. ` +
98
+ `Abra o perfil correto nessa sessão antes de publicar para evitar postar na conta errada.`
99
+ );
100
+ }
101
+
102
+ console.log('Images to upload:', resolvedImages);
103
+
104
+ async function main() {
105
+ console.log('Launching browser in headful mode using the persistent profile...');
106
+ const userDataDir = resolve(args.profileDir);
107
+
108
+ const context = await chromium.launchPersistentContext(userDataDir, {
109
+ headless: false,
110
+ viewport: { width: 1280, height: 800 },
111
+ args: ['--no-sandbox', '--disable-setuid-sandbox']
112
+ });
113
+
114
+ const page = await context.newPage();
115
+
116
+ try {
117
+ console.log('Navigating to Instagram...');
118
+ await page.goto('https://www.instagram.com/', { waitUntil: 'networkidle' });
119
+ await page.waitForTimeout(5000);
120
+
121
+ await waitForInstagramSecurityResolution(page, 'navegação inicial').catch(() => {});
122
+
123
+ // Auto-login if needed
124
+ if (page.url().includes('login') || await page.locator('input[name="username"]').isVisible()) {
125
+ console.log('Not logged in. Automating login...');
126
+ await page.fill('input[name="username"]', username);
127
+ await page.fill('input[name="password"]', password);
128
+ await page.click('button[type="submit"]');
129
+ await page.waitForNavigation({ waitUntil: 'networkidle', timeout: 30000 });
130
+ } else {
131
+ console.log('Already logged in or redirecting...');
132
+ }
133
+
134
+ await waitForInstagramSecurityResolution(page, 'login').catch(() => {});
135
+
136
+ await assertExpectedInstagramOwnerSession(page, profileHandle);
137
+
138
+ await page.waitForTimeout(3000);
139
+
140
+ // Dismiss popups
141
+ for (const popupSelector of [
142
+ 'button:has-text("Agora não")',
143
+ 'button:has-text("Not Now")',
144
+ 'button:has-text("Agora nao")',
145
+ 'div[role="dialog"] button:has-text("Not Now")',
146
+ 'div[role="dialog"] button:has-text("Agora não")'
147
+ ]) {
148
+ try {
149
+ if (await page.locator(popupSelector).isVisible()) {
150
+ console.log(`Dismissing popup: ${popupSelector}`);
151
+ await page.click(popupSelector);
152
+ await page.waitForTimeout(2000);
153
+ }
154
+ } catch (err) {}
155
+ }
156
+
157
+ console.log('Clicking the "Create" button on the sidebar...');
158
+ const createSelectors = [
159
+ 'a:has-text("Criar")',
160
+ 'a:has-text("Create")',
161
+ 'div[role="button"]:has-text("Criar")',
162
+ 'div[role="button"]:has-text("Create")',
163
+ 'svg[aria-label="New post"]',
164
+ 'svg[aria-label="Nova publicação"]',
165
+ '[aria-label="Create"]',
166
+ '[aria-label="Criar"]',
167
+ 'span:has-text("Criar")',
168
+ 'span:has-text("Create")'
169
+ ];
170
+
171
+ let clicked = false;
172
+ for (const selector of createSelectors) {
173
+ try {
174
+ const loc = page.locator(selector).first();
175
+ if (await loc.isVisible()) {
176
+ console.log(`Found create button using selector: ${selector}`);
177
+ await loc.click();
178
+ clicked = true;
179
+ break;
180
+ }
181
+ } catch (err) {}
182
+ }
183
+
184
+ if (!clicked) {
185
+ try {
186
+ const svgLoc = page.locator('svg[aria-label="Nova publicação"], svg[aria-label="New post"]').first();
187
+ if (await svgLoc.isVisible()) {
188
+ console.log('Attempting to click parent of the Plus SVG...');
189
+ await svgLoc.locator('xpath=..').click();
190
+ clicked = true;
191
+ }
192
+ } catch (err) {}
193
+ }
194
+
195
+ console.log('Checking whether Instagram opened a creation menu...');
196
+ const postEntrySelectors = [
197
+ 'div[role="dialog"] span:has-text("Publicação")',
198
+ 'div[role="dialog"] span:has-text("Post")',
199
+ 'div[role="dialog"] div[role="button"]:has-text("Publicação")',
200
+ 'div[role="dialog"] div[role="button"]:has-text("Post")',
201
+ 'span:has-text("Publicação")',
202
+ 'span:has-text("Post")'
203
+ ];
204
+ for (const selector of postEntrySelectors) {
205
+ try {
206
+ let loc = page.locator(selector).first();
207
+ if (await loc.isVisible({ timeout: 1000 })) {
208
+ const roleButton = loc.locator('xpath=ancestor-or-self::*[@role="button"][1]').first();
209
+ if (await roleButton.count() > 0) {
210
+ loc = roleButton;
211
+ }
212
+ console.log(`Selecting post flow using selector: ${selector}`);
213
+ await loc.click();
214
+ await page.waitForTimeout(1500);
215
+ break;
216
+ }
217
+ } catch (err) {}
218
+ }
219
+
220
+ console.log('Waiting for the file input or drag-drop area...');
221
+ try {
222
+ await page.waitForSelector('input[type="file"]', { state: 'attached', timeout: 10000 });
223
+ } catch (e) {
224
+ await waitForInstagramSecurityResolution(page, 'abertura do modal de upload').catch(() => {});
225
+
226
+ const uploadLaunchSelectors = [
227
+ 'button:has-text("Selecionar no computador")',
228
+ 'button:has-text("Selecionar do computador")',
229
+ 'button:has-text("Do computador")',
230
+ 'button:has-text("Select from computer")',
231
+ 'div[role="button"]:has-text("Selecionar no computador")',
232
+ 'div[role="button"]:has-text("Selecionar do computador")',
233
+ 'div[role="button"]:has-text("Do computador")',
234
+ 'div[role="button"]:has-text("Select from computer")',
235
+ 'span:has-text("Selecionar do computador")',
236
+ 'span:has-text("Select from computer")',
237
+ 'div:has-text("Arraste fotos e vídeos aqui")',
238
+ 'div:has-text("Arraste fotos ou vídeos aqui")',
239
+ 'div:has-text("Drag photos and videos here")',
240
+ 'div:has-text("Drag photos or videos here")'
241
+ ];
242
+ for (const selector of uploadLaunchSelectors) {
243
+ try {
244
+ let loc = page.locator(selector).first();
245
+ if (await loc.isVisible({ timeout: 1000 })) {
246
+ const roleButton = loc.locator('xpath=ancestor-or-self::*[@role="button" or self::button or self::label][1]').first();
247
+ if (await roleButton.count() > 0) {
248
+ loc = roleButton;
249
+ }
250
+ console.log(`Opening file chooser using selector: ${selector}`);
251
+ await loc.click();
252
+ await page.waitForTimeout(1500);
253
+ const hasInput = await page.locator('input[type="file"]').count();
254
+ if (hasInput > 0) {
255
+ break;
256
+ }
257
+ }
258
+ } catch (err) {}
259
+ }
260
+
261
+ console.log('\n⚠️ Could not automatically open the creation modal.');
262
+ console.log('👉 Please click the "+" or "Criar / Create" button manually in the opened browser window now.');
263
+ console.log('Waiting up to 180 seconds (3 minutes) for you to do so...\n');
264
+ await page.waitForSelector('input[type="file"]', { state: 'attached', timeout: 180000 });
265
+ }
266
+
267
+ await waitForInstagramSecurityResolution(page, 'espera pelo input de upload').catch(() => {});
268
+
269
+ let uploadInput = page.locator('div[role="dialog"] input[type="file"][multiple]').first();
270
+ if (await uploadInput.count() === 0) {
271
+ uploadInput = page.locator('input[type="file"][multiple]').first();
272
+ }
273
+ if (await uploadInput.count() === 0) {
274
+ throw new Error('Could not find a multi-file upload input in the Instagram publish modal.');
275
+ }
276
+
277
+ console.log('Uploading files...');
278
+ await uploadInput.setInputFiles(resolvedImages);
279
+ await page.waitForTimeout(4000);
280
+
281
+ console.log('Files uploaded. Clicking "Next" / "Avançar"...');
282
+ const nextButtonSelectors = [
283
+ 'div[role="dialog"] button:has-text("Next")',
284
+ 'div[role="dialog"] button:has-text("Avançar")',
285
+ 'div[role="dialog"] div[role="button"]:has-text("Next")',
286
+ 'div[role="dialog"] div[role="button"]:has-text("Avançar")',
287
+ 'button:has-text("Next")',
288
+ 'button:has-text("Avançar")',
289
+ 'div[role="button"]:has-text("Next")',
290
+ 'div[role="button"]:has-text("Avançar")'
291
+ ];
292
+
293
+ let clickedNext = false;
294
+ for (const selector of nextButtonSelectors) {
295
+ try {
296
+ const loc = page.locator(selector).first();
297
+ if (await loc.isVisible()) {
298
+ await loc.click();
299
+ clickedNext = true;
300
+ break;
301
+ }
302
+ } catch (err) {}
303
+ }
304
+ if (!clickedNext) {
305
+ console.log('Next button not clicked automatically. Please click "Next" on the screen.');
306
+ await page.waitForTimeout(5000);
307
+ }
308
+
309
+ await page.waitForTimeout(3000);
310
+
311
+ console.log('Clicking "Next" on the filters screen...');
312
+ clickedNext = false;
313
+ for (const selector of nextButtonSelectors) {
314
+ try {
315
+ const loc = page.locator(selector).first();
316
+ if (await loc.isVisible()) {
317
+ await loc.click();
318
+ clickedNext = true;
319
+ break;
320
+ }
321
+ } catch (err) {}
322
+ }
323
+ if (!clickedNext) {
324
+ console.log('Next button not clicked. Please click "Next" on the screen.');
325
+ await page.waitForTimeout(5000);
326
+ }
327
+
328
+ await page.waitForTimeout(3000);
329
+
330
+ console.log('Writing caption...');
331
+ const captionSelectors = [
332
+ 'div[aria-label="Write a caption..."]',
333
+ 'div[aria-label="Escreva uma legenda..."]',
334
+ 'textarea[aria-label="Write a caption..."]',
335
+ 'textarea[aria-label="Escreva uma legenda..."]',
336
+ '[role="textbox"]'
337
+ ];
338
+
339
+ let captionFilled = false;
340
+ for (const selector of captionSelectors) {
341
+ try {
342
+ const loc = page.locator(selector).first();
343
+ if (await loc.isVisible()) {
344
+ console.log(`Filling caption in: ${selector}`);
345
+ await loc.focus();
346
+ await loc.fill(args.caption);
347
+ captionFilled = true;
348
+ break;
349
+ }
350
+ } catch (err) {}
351
+ }
352
+
353
+ if (!captionFilled) {
354
+ console.log('Caption textbox not found automatically. Please paste the caption manually.');
355
+ await page.waitForTimeout(10000);
356
+ }
357
+
358
+ await page.waitForTimeout(3000);
359
+
360
+ console.log('Clicking "Share" / "Compartilhar"...');
361
+ const shareButtonSelectors = [
362
+ 'button:has-text("Share")',
363
+ 'button:has-text("Compartilhar")',
364
+ 'div[role="dialog"] button:has-text("Share")',
365
+ 'div[role="dialog"] button:has-text("Compartilhar")',
366
+ 'div[role="dialog"] div[role="button"]:has-text("Share")',
367
+ 'div[role="dialog"] div[role="button"]:has-text("Compartilhar")',
368
+ 'div[role="button"]:has-text("Share")',
369
+ 'div[role="button"]:has-text("Compartilhar")'
370
+ ];
371
+
372
+ let clickedShare = false;
373
+ for (const selector of shareButtonSelectors) {
374
+ try {
375
+ const loc = page.locator(selector).first();
376
+ if (await loc.isVisible()) {
377
+ await loc.click();
378
+ clickedShare = true;
379
+ break;
380
+ }
381
+ } catch (err) {}
382
+ }
383
+
384
+ if (!clickedShare) {
385
+ console.log('Share button not found. Please click "Share" manually on the screen.');
386
+ }
387
+
388
+ console.log('Waiting for completion (Your post has been shared)...');
389
+ await page.waitForSelector('text="Your post has been shared"', { timeout: 60000 }).catch(() => {
390
+ return page.waitForSelector('text="Sua publicação foi compartilhada"', { timeout: 60000 });
391
+ }).catch(() => {
392
+ console.log('Timeout waiting for completion message. Assuming success or manual sharing completed.');
393
+ });
394
+
395
+ await page.waitForTimeout(5000);
396
+ console.log('Post published successfully!');
397
+
398
+ // Try to navigate to the brand profile to get the latest post URL
399
+ await page.goto(`https://www.instagram.com/${profileHandle}/`, { waitUntil: 'networkidle' }).catch(() => {});
400
+ await page.waitForTimeout(5000);
401
+
402
+ const postLink = await page.locator('a[href*="/p/"]').first().getAttribute('href').catch(() => null);
403
+ if (postLink) {
404
+ const fullUrl = `https://www.instagram.com${postLink}`;
405
+ const postId = postLink.split('/p/')[1].replace(/\//g, '');
406
+ console.log(`\n🎉 INSTAGRAM SUCCESS URL: ${fullUrl}`);
407
+ console.log(`🎉 INSTAGRAM POST ID: ${postId}`);
408
+ }
409
+
410
+ } catch (err) {
411
+ console.error('An error occurred during Playwright execution:', err);
412
+ } finally {
413
+ console.log('Closing browser context...');
414
+ await context.close();
415
+ }
416
+ }
417
+
418
+ main().catch(console.error);