@daemux/store-automator 0.6.0 → 0.7.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.
@@ -5,14 +5,14 @@
5
5
  },
6
6
  "metadata": {
7
7
  "description": "App Store & Google Play automation for Flutter apps",
8
- "version": "0.6.0"
8
+ "version": "0.7.0"
9
9
  },
10
10
  "plugins": [
11
11
  {
12
12
  "name": "store-automator",
13
13
  "source": "./plugins/store-automator",
14
14
  "description": "3 agents for app store publishing: reviewer, meta-creator, media-designer",
15
- "version": "0.6.0",
15
+ "version": "0.7.0",
16
16
  "keywords": ["flutter", "app-store", "google-play", "fastlane", "codemagic"]
17
17
  }
18
18
  ]
package/bin/cli.mjs CHANGED
@@ -29,6 +29,7 @@ function flagValue(arg, prefix) {
29
29
 
30
30
  const tokenFlags = {
31
31
  '--codemagic-token=': 'codemagicToken',
32
+ '--codemagic-team-id=': 'codemagicTeamId',
32
33
  '--stitch-key=': 'stitchApiKey',
33
34
  '--cloudflare-token=': 'cloudflareToken',
34
35
  '--cloudflare-account-id=': 'cloudflareAccountId',
@@ -87,6 +88,7 @@ Options:
87
88
 
88
89
  MCP Token Flags (skip interactive prompts):
89
90
  --codemagic-token=TOKEN Codemagic API token
91
+ --codemagic-team-id=ID Codemagic Team ID (from Teams page)
90
92
  --stitch-key=KEY Stitch MCP API key
91
93
  --cloudflare-token=TOKEN Cloudflare API token
92
94
  --cloudflare-account-id=ID Cloudflare account ID
@@ -113,7 +115,7 @@ Examples:
113
115
  npx @daemux/store-automator --github-setup Configure GitHub Actions
114
116
 
115
117
  Non-interactive install (CI/CD):
116
- npx @daemux/store-automator --codemagic-token=TOKEN --stitch-key=KEY
118
+ npx @daemux/store-automator --codemagic-token=TOKEN --codemagic-team-id=ID --stitch-key=KEY
117
119
  npx @daemux/store-automator --cloudflare-token=TOKEN --cloudflare-account-id=ID`);
118
120
  process.exit(0);
119
121
  break; // eslint: no-fallthrough
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@daemux/store-automator",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Full App Store & Google Play automation for Flutter apps with Claude Code agents",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "store-automator",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "App Store & Google Play automation agents for Flutter app publishing",
5
5
  "author": {
6
6
  "name": "Daemux"
@@ -44,7 +44,7 @@ The Codemagic pipeline runs two parallel workflows on push to `main`:
44
44
 
45
45
  ### Token Configuration
46
46
 
47
- The Codemagic API token is auto-configured via the codemagic MCP server in `.mcp.json` (set during install). You do not need to resolve the token manually -- all MCP tool calls authenticate automatically. If the codemagic MCP server is missing from `.mcp.json`, instruct the user to re-run `npx @daemux/store-automator` or add `--codemagic-token=TOKEN` to configure it.
47
+ The Codemagic API token and Team ID are auto-configured via the codemagic MCP server in `.mcp.json` (set during install). You do not need to resolve the token manually -- all MCP tool calls authenticate automatically. The `CODEMAGIC_TEAM_ID` env var enables default team resolution for team-scoped tools (`list_builds`, `get_team`, `list_team_members`, `create_variable_group`, `setup_asc_credentials`, `setup_code_signing`). If the codemagic MCP server is missing from `.mcp.json`, instruct the user to re-run `npx @daemux/store-automator` or add `--codemagic-token=TOKEN --codemagic-team-id=ID` to configure it.
48
48
 
49
49
  ### Triggering Builds
50
50
 
@@ -76,20 +76,20 @@ Build states: `queued` -> `preparing` -> `building` -> `testing` -> `publishing`
76
76
  | `start_build` | Start a new build |
77
77
  | `get_build` | Get details of a specific build |
78
78
  | `cancel_build` | Cancel a running build |
79
- | `list_builds` | List builds for a team (V3 API) |
79
+ | `list_builds` | List builds for a team (V3 API, uses default team if omitted) |
80
80
  | `get_artifact_url` | Get the download URL for a build artifact |
81
81
  | `create_public_artifact_url` | Create a time-limited public URL for an artifact |
82
82
  | `list_caches` | List build caches for an application |
83
83
  | `delete_caches` | Delete build caches for an application |
84
- | `setup_asc_credentials` | Create variable group with App Store Connect credentials |
85
- | `setup_code_signing` | Create variable group with iOS code signing cert and profile |
84
+ | `setup_asc_credentials` | Create variable group with ASC credentials (uses default team if omitted) |
85
+ | `setup_code_signing` | Create variable group with iOS signing (uses default team if omitted) |
86
86
  | `get_user` | Get the current authenticated user info |
87
87
  | `list_teams` | List all teams the user belongs to |
88
- | `get_team` | Get details of a specific team |
89
- | `list_team_members` | List members of a specific team |
88
+ | `get_team` | Get details of a specific team (uses default team if omitted) |
89
+ | `list_team_members` | List members of a team (uses default team if omitted) |
90
90
  | `list_variable_groups` | List variable groups for a team or application |
91
91
  | `get_variable_group` | Get details of a specific variable group |
92
- | `create_variable_group` | Create a new variable group |
92
+ | `create_variable_group` | Create a new variable group (uses default team if omitted) |
93
93
  | `update_variable_group` | Update a variable group name or security setting |
94
94
  | `delete_variable_group` | Delete a variable group |
95
95
  | `list_variables` | List variables in a variable group |
@@ -366,7 +366,7 @@ Analyze Firestore usage patterns and optimize queries, indexes, and security rul
366
366
 
367
367
  | File | Purpose |
368
368
  |------|---------|
369
- | `ci.config.yaml` | Single source of truth for all CI/CD config |
369
+ | `ci.config.yaml` | Single source of truth for all CI/CD config (includes team_id, app_id) |
370
370
  | `codemagic.yaml` | Generated from template -- do not edit directly |
371
371
  | `templates/codemagic.template.yaml` | Codemagic workflow template |
372
372
  | `scripts/generate.sh` | Generates codemagic.yaml from ci.config.yaml |
package/src/ci-config.mjs CHANGED
@@ -2,17 +2,21 @@ import { readFileSync, writeFileSync, existsSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
 
4
4
  const CI_CONFIG_FILE = 'ci.config.yaml';
5
- const APP_ID_PATTERN = /^(\s*app_id:\s*)"[^"]*"/m;
5
+ const FIELD_PATTERNS = {
6
+ app_id: /^(\s*app_id:\s*)"[^"]*"/m,
7
+ team_id: /^(\s*team_id:\s*)"[^"]*"/m,
8
+ };
6
9
 
7
- export function writeCiAppId(projectDir, appId) {
10
+ function writeCiField(projectDir, field, value) {
8
11
  const configPath = join(projectDir, CI_CONFIG_FILE);
9
12
  if (!existsSync(configPath)) return false;
10
13
 
11
14
  try {
15
+ const pattern = FIELD_PATTERNS[field];
12
16
  const content = readFileSync(configPath, 'utf8');
13
- if (!APP_ID_PATTERN.test(content)) return false;
17
+ if (!pattern.test(content)) return false;
14
18
 
15
- const updated = content.replace(APP_ID_PATTERN, `$1"${appId}"`);
19
+ const updated = content.replace(pattern, `$1"${value}"`);
16
20
  if (updated === content) return false;
17
21
 
18
22
  writeFileSync(configPath, updated, 'utf8');
@@ -21,3 +25,11 @@ export function writeCiAppId(projectDir, appId) {
21
25
  return false;
22
26
  }
23
27
  }
28
+
29
+ export function writeCiAppId(projectDir, appId) {
30
+ return writeCiField(projectDir, 'app_id', appId);
31
+ }
32
+
33
+ export function writeCiTeamId(projectDir, teamId) {
34
+ return writeCiField(projectDir, 'team_id', teamId);
35
+ }
@@ -50,8 +50,10 @@ export async function findAppByRepo(token, repoUrl) {
50
50
  }) || null;
51
51
  }
52
52
 
53
- export async function addApp(token, repoUrl) {
54
- return cmFetch(token, 'POST', '/apps', { repositoryUrl: repoUrl });
53
+ export async function addApp(token, repoUrl, teamId) {
54
+ const body = { repositoryUrl: repoUrl };
55
+ if (teamId) body.teamId = teamId;
56
+ return cmFetch(token, 'POST', '/apps', body);
55
57
  }
56
58
 
57
59
  export async function startBuild(token, appId, workflowId, branch) {
@@ -1,8 +1,10 @@
1
1
  import { findAppByRepo, addApp, startBuild, getBuildStatus, normalizeRepoUrl } from './codemagic-api.mjs';
2
2
  import { exec, resolveToken } from './utils.mjs';
3
3
  import { execFileSync } from 'child_process';
4
- import { writeCiAppId } from './ci-config.mjs';
5
- import { updateMcpAppId } from './mcp-setup.mjs';
4
+ import { readFileSync } from 'node:fs';
5
+ import { join } from 'node:path';
6
+ import { writeCiAppId, writeCiTeamId } from './ci-config.mjs';
7
+ import { updateMcpAppId, updateMcpTeamId } from './mcp-setup.mjs';
6
8
 
7
9
  const POLL_INTERVAL_MS = 30_000;
8
10
  const POLL_TIMEOUT_MS = 15 * 60 * 1000;
@@ -94,6 +96,24 @@ async function pollBuildStatus(token, buildId) {
94
96
  return 'timeout';
95
97
  }
96
98
 
99
+ function resolveCliTeamId() {
100
+ const prefix = '--codemagic-team-id=';
101
+ for (const arg of process.argv) {
102
+ if (arg.startsWith(prefix)) return arg.slice(prefix.length);
103
+ }
104
+ return undefined;
105
+ }
106
+
107
+ function readCiTeamId() {
108
+ try {
109
+ const content = readFileSync(join(process.cwd(), 'ci.config.yaml'), 'utf8');
110
+ const match = content.match(/^\s*team_id:\s*"([^"]+)"/m);
111
+ return match ? match[1] : undefined;
112
+ } catch {
113
+ return undefined;
114
+ }
115
+ }
116
+
97
117
  export async function runCodemagicSetup(options) {
98
118
  const {
99
119
  tokenArg = '',
@@ -105,8 +125,10 @@ export async function runCodemagicSetup(options) {
105
125
 
106
126
  const token = resolveToken(tokenArg);
107
127
  const repoUrl = resolveRepoUrl();
128
+ const teamId = resolveCliTeamId() || readCiTeamId();
108
129
 
109
130
  console.log(`Repository: ${repoUrl}`);
131
+ if (teamId) console.log(`Team ID: ${teamId}`);
110
132
  console.log('Checking Codemagic for existing app...');
111
133
 
112
134
  let app = await findAppByRepo(token, repoUrl);
@@ -115,7 +137,7 @@ export async function runCodemagicSetup(options) {
115
137
  console.log(`App already registered: ${app.appName || app._id}`);
116
138
  } else {
117
139
  console.log('App not found. Adding to Codemagic...');
118
- app = await addApp(token, repoUrl);
140
+ app = await addApp(token, repoUrl, teamId);
119
141
  console.log(`App added: ${app.appName || app._id}`);
120
142
 
121
143
  console.log('Setting up GitHub webhook...');
@@ -126,6 +148,11 @@ export async function runCodemagicSetup(options) {
126
148
  const appIdWritten = writeCiAppId(process.cwd(), appId);
127
149
  updateMcpAppId(process.cwd(), appId);
128
150
 
151
+ if (teamId) {
152
+ writeCiTeamId(process.cwd(), teamId);
153
+ updateMcpTeamId(process.cwd(), teamId);
154
+ }
155
+
129
156
  if (!trigger) {
130
157
  console.log('\nSetup complete. Use --trigger to start a build.\n');
131
158
  if (appIdWritten) {
package/src/install.mjs CHANGED
@@ -9,10 +9,10 @@ import {
9
9
  } from './utils.mjs';
10
10
  import { injectEnvVars, injectStatusLine } from './settings.mjs';
11
11
  import { promptForTokens } from './prompt.mjs';
12
- import { getMcpServers, writeMcpJson, updateMcpAppId } from './mcp-setup.mjs';
12
+ import { getMcpServers, writeMcpJson, updateMcpAppId, updateMcpTeamId } from './mcp-setup.mjs';
13
13
  import { installClaudeMd, installCiTemplates, installFirebaseTemplates } from './templates.mjs';
14
14
  import { findAppByRepo, addApp, normalizeRepoUrl } from './codemagic-api.mjs';
15
- import { writeCiAppId } from './ci-config.mjs';
15
+ import { writeCiAppId, writeCiTeamId } from './ci-config.mjs';
16
16
 
17
17
  function checkClaudeCli() {
18
18
  const result = exec('command -v claude') || exec('which claude');
@@ -112,7 +112,7 @@ function setupGitHubActions(codemagicToken) {
112
112
  }
113
113
  }
114
114
 
115
- async function setupCodemagicApp(projectDir, codemagicToken) {
115
+ async function setupCodemagicApp(projectDir, codemagicToken, codemagicTeamId) {
116
116
  if (!codemagicToken) return;
117
117
 
118
118
  let repoUrl;
@@ -130,7 +130,7 @@ async function setupCodemagicApp(projectDir, codemagicToken) {
130
130
  try {
131
131
  let app = await findAppByRepo(codemagicToken, repoUrl);
132
132
  if (!app) {
133
- app = await addApp(codemagicToken, repoUrl);
133
+ app = await addApp(codemagicToken, repoUrl, codemagicTeamId);
134
134
  console.log(`Codemagic app created: ${app.appName || app._id}`);
135
135
  } else {
136
136
  console.log(`Codemagic app found: ${app.appName || app._id}`);
@@ -142,6 +142,11 @@ async function setupCodemagicApp(projectDir, codemagicToken) {
142
142
  }
143
143
 
144
144
  updateMcpAppId(projectDir, app._id);
145
+
146
+ if (codemagicTeamId) {
147
+ writeCiTeamId(projectDir, codemagicTeamId);
148
+ updateMcpTeamId(projectDir, codemagicTeamId);
149
+ }
145
150
  } catch (err) {
146
151
  console.log(`Codemagic auto-setup skipped: ${err.message || err}`);
147
152
  }
@@ -178,7 +183,7 @@ export async function runInstall(scope, isPostinstall = false, cliTokens = {}) {
178
183
  installCiTemplates(projectDir, packageDir);
179
184
  installFirebaseTemplates(projectDir, packageDir);
180
185
 
181
- await setupCodemagicApp(projectDir, tokens.codemagicToken);
186
+ await setupCodemagicApp(projectDir, tokens.codemagicToken, tokens.codemagicTeamId);
182
187
 
183
188
  const scopeLabel = scope === 'user' ? 'global' : 'project';
184
189
  console.log(`Configuring ${scopeLabel} settings...`);
package/src/mcp-setup.mjs CHANGED
@@ -34,6 +34,7 @@ export function getMcpServers(tokens) {
34
34
 
35
35
  if (tokens.codemagicToken) {
36
36
  const codemagicEnv = { CODEMAGIC_API_TOKEN: tokens.codemagicToken };
37
+ if (tokens.codemagicTeamId) codemagicEnv.CODEMAGIC_TEAM_ID = tokens.codemagicTeamId;
37
38
  if (tokens.codemagicAppId) codemagicEnv.CODEMAGIC_APP_ID = tokens.codemagicAppId;
38
39
  servers.codemagic = {
39
40
  command: 'npx',
@@ -77,14 +78,14 @@ export function writeMcpJson(projectDir, servers) {
77
78
  }
78
79
  }
79
80
 
80
- export function updateMcpAppId(projectDir, appId) {
81
+ function updateMcpEnvVar(projectDir, envKey, value) {
81
82
  const mcpPath = join(projectDir, '.mcp.json');
82
83
  if (!existsSync(mcpPath)) return false;
83
84
 
84
85
  try {
85
86
  const data = readJson(mcpPath);
86
87
  if (!data.mcpServers?.codemagic?.env) return false;
87
- data.mcpServers.codemagic.env.CODEMAGIC_APP_ID = appId;
88
+ data.mcpServers.codemagic.env[envKey] = value;
88
89
  writeJson(mcpPath, data);
89
90
  return true;
90
91
  } catch {
@@ -92,6 +93,14 @@ export function updateMcpAppId(projectDir, appId) {
92
93
  }
93
94
  }
94
95
 
96
+ export function updateMcpAppId(projectDir, appId) {
97
+ return updateMcpEnvVar(projectDir, 'CODEMAGIC_APP_ID', appId);
98
+ }
99
+
100
+ export function updateMcpTeamId(projectDir, teamId) {
101
+ return updateMcpEnvVar(projectDir, 'CODEMAGIC_TEAM_ID', teamId);
102
+ }
103
+
95
104
  export function removeMcpServers(projectDir) {
96
105
  const mcpPath = join(projectDir, '.mcp.json');
97
106
  if (!existsSync(mcpPath)) return;
package/src/prompt.mjs CHANGED
@@ -17,7 +17,8 @@ function allTokensProvided(cliTokens) {
17
17
  cliTokens.stitchApiKey !== undefined &&
18
18
  cliTokens.cloudflareToken !== undefined &&
19
19
  cliTokens.cloudflareAccountId !== undefined &&
20
- cliTokens.codemagicToken !== undefined
20
+ cliTokens.codemagicToken !== undefined &&
21
+ cliTokens.codemagicTeamId !== undefined
21
22
  );
22
23
  }
23
24
 
@@ -27,6 +28,7 @@ export async function promptForTokens(cliTokens = {}) {
27
28
  cloudflareToken: cliTokens.cloudflareToken ?? '',
28
29
  cloudflareAccountId: cliTokens.cloudflareAccountId ?? '',
29
30
  codemagicToken: cliTokens.codemagicToken ?? '',
31
+ codemagicTeamId: cliTokens.codemagicTeamId ?? '',
30
32
  };
31
33
 
32
34
  if (allTokensProvided(cliTokens)) {
@@ -79,6 +81,13 @@ export async function promptForTokens(cliTokens = {}) {
79
81
  );
80
82
  }
81
83
 
84
+ if (result.codemagicToken && cliTokens.codemagicTeamId === undefined) {
85
+ result.codemagicTeamId = await ask(
86
+ rl,
87
+ 'Codemagic Team ID (optional, from Teams page): '
88
+ );
89
+ }
90
+
82
91
  return result;
83
92
  } finally {
84
93
  rl.close();
@@ -59,6 +59,7 @@ web:
59
59
  # Find app_id in your Codemagic dashboard URL: codemagic.io/app/{app_id}
60
60
  # API token is stored in .mcp.json (codemagic MCP server, set during install)
61
61
  codemagic:
62
+ team_id: "" # Team ID from Codemagic Teams page
62
63
  app_id: ""
63
64
  workflows:
64
65
  - ios-release