adapty 0.9.1 → 0.9.3
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 +26 -0
- package/dist/cli/commands/migrations/run/command.d.ts +4 -1
- package/dist/cli/commands/migrations/run/command.js +49 -5
- package/dist/cli/commands/migrations/run/lib/agent.d.ts +24 -0
- package/dist/cli/commands/migrations/run/lib/agent.js +32 -0
- package/dist/cli/commands/skills/install/command.d.ts +14 -0
- package/dist/cli/commands/skills/install/command.js +78 -0
- package/dist/cli/commands/skills/install/index.d.ts +1 -0
- package/dist/cli/commands/skills/install/index.js +1 -0
- package/dist/cli/commands/skills/install/lib/agents.d.ts +33 -0
- package/dist/cli/commands/skills/install/lib/agents.js +91 -0
- package/dist/cli/commands/skills/install/lib/choose.d.ts +7 -0
- package/dist/cli/commands/skills/install/lib/choose.js +62 -0
- package/dist/cli/commands/skills/install/lib/render.d.ts +2 -0
- package/dist/cli/commands/skills/install/lib/render.js +16 -0
- package/dist/cli/views/migrations/envelope/action-block.js +1 -1
- package/dist/commands/skills/install.d.ts +1 -0
- package/dist/commands/skills/install.js +2 -0
- package/dist/lib/asa-schemas.d.ts +6 -1
- package/dist/sdk/adapty/migrations/model.d.ts +4 -1
- package/oclif.manifest.json +232 -179
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -185,6 +185,16 @@ requiring confirmation exit **6** without it; there is no interactive prompt. Th
|
|
|
185
185
|
status before refusing, but does not send the action request. After an action, check status again.
|
|
186
186
|
After `revision_conflict`, read status and review the current action, input and confirmation before retrying.
|
|
187
187
|
|
|
188
|
+
For an `agent` action (`migrate_code`), run it from the app's git repository, ideally from your coding agent. It
|
|
189
|
+
writes the migration's data to `.git/adapty/<migration ID>/` and prints what the agent needs: a branch to work on (`adapty-migrate`), which skill to use, where the data
|
|
190
|
+
is, and the server's guide. Outside an agent, paste the output into one. The agent reports back by running the
|
|
191
|
+
action again with `--input`:
|
|
192
|
+
|
|
193
|
+
```sh
|
|
194
|
+
adapty migrations run migrate_code
|
|
195
|
+
adapty migrations run migrate_code --input '{"summary": "Swapped the SDK; ADAPTY_SETUP.md leaves 3 steps"}'
|
|
196
|
+
```
|
|
197
|
+
|
|
188
198
|
For an `external` action, complete the step in the browser, then check status:
|
|
189
199
|
|
|
190
200
|
```sh
|
|
@@ -249,6 +259,22 @@ adapty migrations steps -m mig_7x2 --json | jq '.steps'
|
|
|
249
259
|
adapty migrations show RESOURCE -m mig_7x2 --json | jq '.result'
|
|
250
260
|
```
|
|
251
261
|
|
|
262
|
+
### Skills
|
|
263
|
+
|
|
264
|
+
Install the Adapty skills into the coding agents on this machine. Claude Code, Codex and Gemini CLI found on
|
|
265
|
+
`PATH` get them through their own plugin or skills command:
|
|
266
|
+
|
|
267
|
+
```sh
|
|
268
|
+
adapty skills install
|
|
269
|
+
adapty skills install --agent claude-code
|
|
270
|
+
adapty skills install --yes
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
When several agents are found, the command asks which to install into; with no one to ask (a pipe or
|
|
274
|
+
`--json`), pass `--agent` or `--yes` for all of them, or it exits **2**. When none is found, it runs
|
|
275
|
+
`npx skills add adaptyteam/adapty-skills --all --global`, which covers the other agents. Restart an agent to
|
|
276
|
+
load the skills. Exit **1** means an install failed.
|
|
277
|
+
|
|
252
278
|
### Apple Search Ads
|
|
253
279
|
|
|
254
280
|
Apple Search Ads commands live under `adapty asa` and talk to the ASA service rather than the Developer
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { MigrationCommand } from '../../../base/adapty/index.js';
|
|
2
|
+
import type { Handoff } from './lib/agent.js';
|
|
2
3
|
import type { Envelope } from '../../../../sdk/adapty/index.js';
|
|
3
4
|
export default class Run extends MigrationCommand {
|
|
4
5
|
static summary: string;
|
|
@@ -18,8 +19,10 @@ export default class Run extends MigrationCommand {
|
|
|
18
19
|
'no-browser': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
19
20
|
migration: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
20
21
|
};
|
|
21
|
-
run(): Promise<Envelope>;
|
|
22
|
+
run(): Promise<Envelope | Handoff>;
|
|
22
23
|
private prepare;
|
|
24
|
+
/** The agent that ran this command, or the one the developer pastes the output into, does the work. */
|
|
25
|
+
private handOff;
|
|
23
26
|
private runExternalAction;
|
|
24
27
|
private runInputAction;
|
|
25
28
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
1
3
|
import { Args, Flags } from '@oclif/core';
|
|
2
4
|
import { MigrationCommand } from '../../../base/adapty/index.js';
|
|
3
5
|
import { CliError, exitCode } from '../../../errors.js';
|
|
@@ -5,10 +7,11 @@ import { migrationFlags } from '../../../input/migration.js';
|
|
|
5
7
|
import { renderEnvelope } from '../../../views/migrations/envelope/envelope.js';
|
|
6
8
|
import { actionView } from './lib/action-view.js';
|
|
7
9
|
import { findAction, unknownActionMessage, unsupportedActionMessage } from './lib/actions.js';
|
|
10
|
+
import { buildHandoff, PATH_SEGMENT, repository } from './lib/agent.js';
|
|
8
11
|
import { readActionInput } from './lib/input.js';
|
|
9
12
|
import { openLink } from './lib/open-link.js';
|
|
10
13
|
export default class Run extends MigrationCommand {
|
|
11
|
-
static summary = 'Run an input action
|
|
14
|
+
static summary = 'Run an input action, open an external action link, or hand an agent action to a coding agent';
|
|
12
15
|
static description = [
|
|
13
16
|
'Choose an action ID from next_actions or available_actions in `adapty migrations status -m ID --json`.',
|
|
14
17
|
'Replace ACTION_ID in the examples with an offered action ID.',
|
|
@@ -21,7 +24,12 @@ export default class Run extends MigrationCommand {
|
|
|
21
24
|
'Pipes and --json require --open to launch a browser; BROWSER=none disables it.',
|
|
22
25
|
'File uploads are not supported; use the dashboard or an offered Cloud Export action.',
|
|
23
26
|
'',
|
|
24
|
-
'
|
|
27
|
+
'An agent action writes the resources in reads to .git/adapty/<migration ID>/ in the app\'s git repository and',
|
|
28
|
+
'prints what a coding agent needs to do it. Run it from the agent, or paste its output into one. The agent',
|
|
29
|
+
'reports the result by running the action again with --input.',
|
|
30
|
+
'',
|
|
31
|
+
'With --json, returns the full migration response (before the browser step for external actions). For an agent',
|
|
32
|
+
'action without --input, returns { action_id, files, instructions }: the files written and the text for the agent.',
|
|
25
33
|
'After a revision_conflict error, read status and review the action before retrying.',
|
|
26
34
|
].join('\n');
|
|
27
35
|
static examples = [
|
|
@@ -89,6 +97,9 @@ export default class Run extends MigrationCommand {
|
|
|
89
97
|
if (context.href !== undefined) {
|
|
90
98
|
return this.runExternalAction(context, context.href);
|
|
91
99
|
}
|
|
100
|
+
if (context.action.kind === 'agent' && context.input === undefined) {
|
|
101
|
+
return this.handOff(context);
|
|
102
|
+
}
|
|
92
103
|
return this.runInputAction(context);
|
|
93
104
|
}
|
|
94
105
|
async prepare() {
|
|
@@ -108,20 +119,53 @@ export default class Run extends MigrationCommand {
|
|
|
108
119
|
}
|
|
109
120
|
return {
|
|
110
121
|
action, envelope, flags, href, selection,
|
|
111
|
-
input: fromStdin && action.kind === 'input' ? await readActionInput(flags) : input,
|
|
122
|
+
input: fromStdin && (action.kind === 'input' || action.kind === 'agent') ? await readActionInput(flags) : input,
|
|
112
123
|
};
|
|
113
124
|
}
|
|
125
|
+
/** The agent that ran this command, or the one the developer pastes the output into, does the work. */
|
|
126
|
+
async handOff({ action, envelope }) {
|
|
127
|
+
const repo = await repository(process.cwd());
|
|
128
|
+
if (repo === undefined) {
|
|
129
|
+
throw new CliError(`\`${action.action_id}\` works on the app code: run it from the app's git repository.`, exitCode.usage, 'not_a_repository');
|
|
130
|
+
}
|
|
131
|
+
const migrationId = envelope.migration.id;
|
|
132
|
+
if (!PATH_SEGMENT.test(migrationId)) {
|
|
133
|
+
throw new CliError(`The server named a migration \`${migrationId}\` that cannot be a folder name.`, 1, 'migration_id_invalid');
|
|
134
|
+
}
|
|
135
|
+
// A folder per migration: two migrations handed off in one repository never read each other's data.
|
|
136
|
+
const dir = join(repo.gitDir, 'adapty', migrationId);
|
|
137
|
+
const files = [];
|
|
138
|
+
const guides = [];
|
|
139
|
+
await mkdir(dir, { recursive: true });
|
|
140
|
+
for (const name of action.reads) {
|
|
141
|
+
const { result } = await this.adapty.migrations.resource(migrationId, name);
|
|
142
|
+
if (typeof result === 'object' && result !== null && 'markdown' in result && typeof result.markdown === 'string') {
|
|
143
|
+
guides.push(result.markdown);
|
|
144
|
+
}
|
|
145
|
+
else if (PATH_SEGMENT.test(name)) {
|
|
146
|
+
const file = join(dir, `${name}.json`);
|
|
147
|
+
await writeFile(file, `${JSON.stringify(result, null, 2)}\n`);
|
|
148
|
+
files.push(`\`${name}\`: ${file}`);
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
throw new CliError(`The server named a resource \`${name}\` that cannot be a file name.`, 1, 'resource_name_invalid');
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
const handoff = buildHandoff({ actionId: action.action_id, files, guides });
|
|
155
|
+
this.render(handoff, ({ instructions }) => instructions);
|
|
156
|
+
return handoff;
|
|
157
|
+
}
|
|
114
158
|
async runExternalAction(context, href) {
|
|
115
159
|
this.render({ action: context.action, migrationId: context.envelope.migration.id }, actionView);
|
|
116
160
|
await openLink(href, context.flags, this.interactive);
|
|
117
161
|
return context.envelope;
|
|
118
162
|
}
|
|
119
163
|
async runInputAction({ action, envelope, flags, input, selection }) {
|
|
120
|
-
if (action.kind !== 'input' && action.kind !== 'upload' && action.kind !== 'external') {
|
|
164
|
+
if (action.kind !== 'input' && action.kind !== 'upload' && action.kind !== 'external' && action.kind !== 'agent') {
|
|
121
165
|
this.render({ action, migrationId: envelope.migration.id }, actionView);
|
|
122
166
|
return envelope;
|
|
123
167
|
}
|
|
124
|
-
if (action.kind !== 'input') {
|
|
168
|
+
if (action.kind !== 'input' && action.kind !== 'agent') {
|
|
125
169
|
throw new CliError(unsupportedActionMessage(action), exitCode.usage, 'action_unsupported');
|
|
126
170
|
}
|
|
127
171
|
if (action.confirm !== null && !flags.yes) {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** In a worktree or a submodule `.git` is a file, so the git directory comes from git itself. */
|
|
2
|
+
export declare const repository: (cwd: string) => Promise<{
|
|
3
|
+
gitDir: string;
|
|
4
|
+
} | undefined>;
|
|
5
|
+
export type HandoffInput = {
|
|
6
|
+
actionId: string;
|
|
7
|
+
/** `name`: absolute path, one per resource written to a file. */
|
|
8
|
+
files: string[];
|
|
9
|
+
/** Markdown resources, the server's instructions for this migration, handed to the agent as they are. */
|
|
10
|
+
guides: string[];
|
|
11
|
+
};
|
|
12
|
+
/** What `--json` returns for an agent action, and what the human view prints. */
|
|
13
|
+
export type Handoff = {
|
|
14
|
+
action_id: string;
|
|
15
|
+
files: string[];
|
|
16
|
+
instructions: string;
|
|
17
|
+
};
|
|
18
|
+
/** A resource name or a migration ID becomes a path segment: no separators, no dots to climb out with. */
|
|
19
|
+
export declare const PATH_SEGMENT: RegExp;
|
|
20
|
+
/**
|
|
21
|
+
* The server says what to do; the CLI adds the one thing only it knows, where it put the data. Paths
|
|
22
|
+
* are absolute, so they open from any folder the agent runs in, a monorepo's app folder included.
|
|
23
|
+
*/
|
|
24
|
+
export declare const buildHandoff: ({ actionId, files, guides }: HandoffInput) => Handoff;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { promisify } from 'node:util';
|
|
4
|
+
const git = promisify(execFile);
|
|
5
|
+
/** In a worktree or a submodule `.git` is a file, so the git directory comes from git itself. */
|
|
6
|
+
export const repository = async (cwd) => {
|
|
7
|
+
try {
|
|
8
|
+
const { stdout } = await git('git', ['rev-parse', '--absolute-git-dir'], { cwd });
|
|
9
|
+
const gitDir = stdout.trim();
|
|
10
|
+
// git prints forward slashes on Windows too: resolve() gives the platform's own form.
|
|
11
|
+
return gitDir === '' ? undefined : { gitDir: resolve(gitDir) };
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
/** A resource name or a migration ID becomes a path segment: no separators, no dots to climb out with. */
|
|
18
|
+
export const PATH_SEGMENT = /^[a-z0-9][a-z0-9-]*$/;
|
|
19
|
+
/**
|
|
20
|
+
* The server says what to do; the CLI adds the one thing only it knows, where it put the data. Paths
|
|
21
|
+
* are absolute, so they open from any folder the agent runs in, a monorepo's app folder included.
|
|
22
|
+
*/
|
|
23
|
+
export const buildHandoff = ({ actionId, files, guides }) => ({
|
|
24
|
+
action_id: actionId,
|
|
25
|
+
files,
|
|
26
|
+
instructions: [
|
|
27
|
+
'adapty-cli wrote this migration\'s data to:',
|
|
28
|
+
...files.map(file => `- ${file}`),
|
|
29
|
+
'',
|
|
30
|
+
...guides.map(guide => guide.trim()),
|
|
31
|
+
].join('\n'),
|
|
32
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { BaseCommand } from '../../../base/base-command.js';
|
|
2
|
+
import type { InstallResult } from './lib/agents.js';
|
|
3
|
+
export default class SkillsInstall extends BaseCommand {
|
|
4
|
+
static description: string;
|
|
5
|
+
static examples: string[];
|
|
6
|
+
static flags: {
|
|
7
|
+
agent: import("@oclif/core/interfaces").OptionFlag<("claude-code" | "codex" | "gemini-cli")[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
9
|
+
};
|
|
10
|
+
run(): Promise<InstallResult[]>;
|
|
11
|
+
private targets;
|
|
12
|
+
/** The skills CLI runs code from npm, so it needs a yes from someone at a terminal, whatever the flags say. */
|
|
13
|
+
private fallbackTarget;
|
|
14
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import { BaseCommand } from '../../../base/base-command.js';
|
|
3
|
+
import { CliError, exitCode } from '../../../errors.js';
|
|
4
|
+
import { agentIds, agents, fallback, FALLBACK_COMMAND, install, onPath, runQuietly } from './lib/agents.js';
|
|
5
|
+
import { choose, confirm } from './lib/choose.js';
|
|
6
|
+
import { renderInstall } from './lib/render.js';
|
|
7
|
+
export default class SkillsInstall extends BaseCommand {
|
|
8
|
+
static description = 'Install the Adapty skills into the coding agents on this machine';
|
|
9
|
+
static examples = [
|
|
10
|
+
'<%= config.bin %> skills install',
|
|
11
|
+
'<%= config.bin %> skills install --agent claude-code',
|
|
12
|
+
'<%= config.bin %> skills install --yes',
|
|
13
|
+
];
|
|
14
|
+
static flags = {
|
|
15
|
+
agent: Flags.option({
|
|
16
|
+
description: 'Install into this agent only. Repeat for several.',
|
|
17
|
+
multiple: true,
|
|
18
|
+
options: agentIds,
|
|
19
|
+
})(),
|
|
20
|
+
yes: Flags.boolean({
|
|
21
|
+
char: 'y',
|
|
22
|
+
description: 'Install into every agent found without asking; required when several are found and no one can answer. '
|
|
23
|
+
+ 'Never runs the npx fallback, which is always asked for',
|
|
24
|
+
}),
|
|
25
|
+
};
|
|
26
|
+
async run() {
|
|
27
|
+
const { flags } = await this.parse(SkillsInstall);
|
|
28
|
+
const named = flags.agent !== undefined;
|
|
29
|
+
const chosen = named ? agents.filter(agent => flags.agent?.includes(agent.id)) : agents;
|
|
30
|
+
const results = [];
|
|
31
|
+
const found = [];
|
|
32
|
+
for (const agent of chosen) {
|
|
33
|
+
if (await onPath(agent.bin)) {
|
|
34
|
+
found.push(agent);
|
|
35
|
+
}
|
|
36
|
+
else if (named) {
|
|
37
|
+
results.push({ agent: agent.id, error: `\`${agent.bin}\` is not on PATH`, installed: false });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
for (const agent of await this.targets({ found, named, yes: flags.yes })) {
|
|
41
|
+
results.push(await install(agent, runQuietly));
|
|
42
|
+
}
|
|
43
|
+
this.render(results, renderInstall);
|
|
44
|
+
if (results.some(result => !result.installed)) {
|
|
45
|
+
process.exitCode = 1;
|
|
46
|
+
}
|
|
47
|
+
return results;
|
|
48
|
+
}
|
|
49
|
+
async targets({ found, named, yes }) {
|
|
50
|
+
if (found.length === 0 && !named) {
|
|
51
|
+
return this.fallbackTarget();
|
|
52
|
+
}
|
|
53
|
+
if (named || yes || found.length === 1) {
|
|
54
|
+
return found;
|
|
55
|
+
}
|
|
56
|
+
if (!this.interactive || !process.stdin.isTTY) {
|
|
57
|
+
const names = found.map(agent => agent.name).join(', ');
|
|
58
|
+
throw new CliError(`Found ${names}. Choose with --agent, or pass --yes to install into all of them.`, exitCode.usage, 'agent_choice_required');
|
|
59
|
+
}
|
|
60
|
+
return choose(found, this.signal);
|
|
61
|
+
}
|
|
62
|
+
/** The skills CLI runs code from npm, so it needs a yes from someone at a terminal, whatever the flags say. */
|
|
63
|
+
async fallbackTarget() {
|
|
64
|
+
const bins = agents.map(agent => `\`${agent.bin}\``).join(', ');
|
|
65
|
+
if (!(await onPath(fallback.bin))) {
|
|
66
|
+
throw new CliError(`Found none of ${bins}, and no \`npx\` to run the skills CLI with.`, 1, 'no_agent_found');
|
|
67
|
+
}
|
|
68
|
+
if (!this.interactive || !process.stdin.isTTY) {
|
|
69
|
+
throw new CliError(`Found none of ${bins}. To install with the skills CLI, run \`${FALLBACK_COMMAND}\` yourself.`, exitCode.usage, 'fallback_confirmation_required');
|
|
70
|
+
}
|
|
71
|
+
const question = `Found none of ${bins}. Run \`${FALLBACK_COMMAND}\`? It downloads the skills CLI from npm `
|
|
72
|
+
+ 'and installs the Adapty skills into every agent it finds.';
|
|
73
|
+
if (!(await confirm(question, this.signal))) {
|
|
74
|
+
throw new CliError('Nothing installed.', 1, 'fallback_declined');
|
|
75
|
+
}
|
|
76
|
+
return [fallback];
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './command.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './command.js';
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export declare const agentIds: readonly ["claude-code", "codex", "gemini-cli"];
|
|
2
|
+
export type AgentId = (typeof agentIds)[number];
|
|
3
|
+
export type Agent<Id extends string = AgentId | 'other'> = {
|
|
4
|
+
bin: string;
|
|
5
|
+
id: Id;
|
|
6
|
+
name: string;
|
|
7
|
+
steps: string[][];
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Each agent's own installer, the same commands the docs' "Install agent tools" modal shows. A
|
|
11
|
+
* marketplace step fails harmlessly when the marketplace is already there, so only the last step
|
|
12
|
+
* decides the outcome.
|
|
13
|
+
*/
|
|
14
|
+
export declare const agents: readonly Agent<AgentId>[];
|
|
15
|
+
/**
|
|
16
|
+
* For a machine where none of the agents above is found: the skills CLI detects the rest itself and
|
|
17
|
+
* installs into every agent it finds. It runs code from npm, so it only ever runs after a yes.
|
|
18
|
+
*/
|
|
19
|
+
export declare const fallback: Agent;
|
|
20
|
+
export declare const FALLBACK_COMMAND = "npx skills@1.7.0 add adaptyteam/adapty-skills --all --global";
|
|
21
|
+
export type Run = (bin: string, args: readonly string[]) => Promise<{
|
|
22
|
+
code: number | null;
|
|
23
|
+
stderr: string;
|
|
24
|
+
}>;
|
|
25
|
+
export type InstallResult = {
|
|
26
|
+
agent: Agent['id'];
|
|
27
|
+
error?: string;
|
|
28
|
+
installed: boolean;
|
|
29
|
+
warning?: string;
|
|
30
|
+
};
|
|
31
|
+
export declare const runQuietly: Run;
|
|
32
|
+
export declare const onPath: (bin: string) => Promise<boolean>;
|
|
33
|
+
export declare const install: (agent: Agent, run: Run) => Promise<InstallResult>;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { access, constants } from 'node:fs/promises';
|
|
3
|
+
import { delimiter, join } from 'node:path';
|
|
4
|
+
const SOURCE = 'adaptyteam/adapty-skills';
|
|
5
|
+
const STDERR_TAIL_LINES = 5;
|
|
6
|
+
export const agentIds = ['claude-code', 'codex', 'gemini-cli'];
|
|
7
|
+
/**
|
|
8
|
+
* Each agent's own installer, the same commands the docs' "Install agent tools" modal shows. A
|
|
9
|
+
* marketplace step fails harmlessly when the marketplace is already there, so only the last step
|
|
10
|
+
* decides the outcome.
|
|
11
|
+
*/
|
|
12
|
+
export const agents = [
|
|
13
|
+
{
|
|
14
|
+
bin: 'claude',
|
|
15
|
+
id: 'claude-code',
|
|
16
|
+
name: 'Claude Code',
|
|
17
|
+
steps: [['plugin', 'marketplace', 'add', SOURCE], ['plugin', 'install', 'adapty-skills@adapty']],
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
bin: 'codex',
|
|
21
|
+
id: 'codex',
|
|
22
|
+
name: 'Codex',
|
|
23
|
+
steps: [['plugin', 'marketplace', 'add', SOURCE], ['plugin', 'add', 'adapty-skills@adapty']],
|
|
24
|
+
},
|
|
25
|
+
{ bin: 'gemini', id: 'gemini-cli', name: 'Gemini CLI', steps: [['skills', 'install', `https://github.com/${SOURCE}`]] },
|
|
26
|
+
];
|
|
27
|
+
/** Pinned: the fallback runs this package from npm, so a new release is taken on purpose, not by accident. */
|
|
28
|
+
const SKILLS_CLI = 'skills@1.7.0';
|
|
29
|
+
/**
|
|
30
|
+
* For a machine where none of the agents above is found: the skills CLI detects the rest itself and
|
|
31
|
+
* installs into every agent it finds. It runs code from npm, so it only ever runs after a yes.
|
|
32
|
+
*/
|
|
33
|
+
export const fallback = {
|
|
34
|
+
bin: 'npx',
|
|
35
|
+
id: 'other',
|
|
36
|
+
name: 'Other agents (skills CLI)',
|
|
37
|
+
steps: [['--yes', SKILLS_CLI, 'add', SOURCE, '--all', '--global']],
|
|
38
|
+
};
|
|
39
|
+
export const FALLBACK_COMMAND = `npx ${SKILLS_CLI} add ${SOURCE} --all --global`;
|
|
40
|
+
// A Windows agent is a .cmd shim that only a shell resolves.
|
|
41
|
+
const windows = process.platform === 'win32';
|
|
42
|
+
export const runQuietly = async (bin, args) => new Promise((resolve) => {
|
|
43
|
+
const child = spawn(bin, args, { env: { ...process.env, NO_COLOR: '1' }, shell: windows, stdio: ['ignore', 'ignore', 'pipe'] });
|
|
44
|
+
let stderr = '';
|
|
45
|
+
child.stderr.on('data', (chunk) => {
|
|
46
|
+
stderr += chunk.toString();
|
|
47
|
+
});
|
|
48
|
+
child.on('error', (error) => {
|
|
49
|
+
resolve({ code: null, stderr: error.message });
|
|
50
|
+
});
|
|
51
|
+
child.on('close', (code) => {
|
|
52
|
+
resolve({ code, stderr });
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
export const onPath = async (bin) => {
|
|
56
|
+
const extensions = windows ? (process.env.PATHEXT ?? '.EXE;.CMD').split(';') : [''];
|
|
57
|
+
const candidates = (process.env.PATH ?? '').split(delimiter).filter(Boolean)
|
|
58
|
+
.flatMap(dir => extensions.map(extension => join(dir, bin + extension)));
|
|
59
|
+
for (const candidate of candidates) {
|
|
60
|
+
try {
|
|
61
|
+
await access(candidate, constants.X_OK);
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// not in this directory
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
};
|
|
70
|
+
const tailOf = (stderr) => stderr.trim().split('\n').slice(-STDERR_TAIL_LINES).join('\n');
|
|
71
|
+
export const install = async (agent, run) => {
|
|
72
|
+
const results = [];
|
|
73
|
+
for (const step of agent.steps) {
|
|
74
|
+
results.push(await run(agent.bin, step));
|
|
75
|
+
}
|
|
76
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- every agent has at least one step
|
|
77
|
+
const last = results.at(-1);
|
|
78
|
+
if (last.code !== 0) {
|
|
79
|
+
const error = tailOf(last.stderr) || `\`${agent.bin}\` exited with code ${String(last.code)}`;
|
|
80
|
+
return { agent: agent.id, error, installed: false };
|
|
81
|
+
}
|
|
82
|
+
// A marketplace that is already there fails the step, whether it is ours or another source under
|
|
83
|
+
// the same name; only the second installs someone else's skills, and the CLI cannot tell them apart.
|
|
84
|
+
const marketplace = results.length > 1 ? results[0] : undefined;
|
|
85
|
+
if (marketplace !== undefined && marketplace.code !== 0) {
|
|
86
|
+
const warning = `adding the marketplace failed (${tailOf(marketplace.stderr) || 'no message'}). If an \`adapty\` `
|
|
87
|
+
+ `marketplace already existed, the skills came from its source: check it with \`${agent.bin} plugin marketplace list\`.`;
|
|
88
|
+
return { agent: agent.id, installed: true, warning };
|
|
89
|
+
}
|
|
90
|
+
return { agent: agent.id, installed: true };
|
|
91
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Agent } from './agents.js';
|
|
2
|
+
/** `1,3` or `1 3` picks those; an empty answer picks all of them. Anything else is asked again. */
|
|
3
|
+
export declare const parseChoice: (answer: string, count: number) => number[] | undefined;
|
|
4
|
+
export declare const choose: (found: readonly Agent[], signal: AbortSignal) => Promise<Agent[]>;
|
|
5
|
+
/** An empty answer is a no: the question guards a command that runs code from npm. */
|
|
6
|
+
export declare const parseConsent: (answer: string) => boolean | undefined;
|
|
7
|
+
export declare const confirm: (question: string, signal: AbortSignal) => Promise<boolean>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { createInterface } from 'node:readline/promises';
|
|
2
|
+
import { CliError, exitCode } from '../../../../errors.js';
|
|
3
|
+
/** `1,3` or `1 3` picks those; an empty answer picks all of them. Anything else is asked again. */
|
|
4
|
+
export const parseChoice = (answer, count) => {
|
|
5
|
+
const trimmed = answer.trim();
|
|
6
|
+
if (trimmed === '') {
|
|
7
|
+
return Array.from({ length: count }, (_, index) => index);
|
|
8
|
+
}
|
|
9
|
+
const picked = trimmed.split(/[\s,]+/).map(Number);
|
|
10
|
+
if (picked.some(number => !Number.isInteger(number) || number < 1 || number > count)) {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
return [...new Set(picked)].sort((a, b) => a - b).map(number => number - 1);
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Asked on stderr, so the answer never mixes into what a program reads from stdout. A terminal's
|
|
17
|
+
* Ctrl+C reaches readline, not the process, so the prompt turns it into the usual exit 130 itself.
|
|
18
|
+
*/
|
|
19
|
+
const ask = async ({ question, retry, signal }, parse) => {
|
|
20
|
+
const reader = createInterface({ input: process.stdin, output: process.stderr });
|
|
21
|
+
const interrupted = new AbortController();
|
|
22
|
+
reader.on('SIGINT', () => {
|
|
23
|
+
interrupted.abort();
|
|
24
|
+
});
|
|
25
|
+
try {
|
|
26
|
+
for (;;) {
|
|
27
|
+
const answer = await reader.question(question, { signal: AbortSignal.any([signal, interrupted.signal]) });
|
|
28
|
+
const parsed = parse(answer);
|
|
29
|
+
if (parsed !== undefined) {
|
|
30
|
+
return parsed;
|
|
31
|
+
}
|
|
32
|
+
process.stderr.write(`${retry}\n`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
if (signal.aborted || interrupted.signal.aborted) {
|
|
37
|
+
throw new CliError('Cancelled.', exitCode.cancelled, undefined, { cause: error });
|
|
38
|
+
}
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
finally {
|
|
42
|
+
reader.close();
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
export const choose = async (found, signal) => {
|
|
46
|
+
const list = found.map((agent, index) => ` ${String(index + 1)}. ${agent.name}`).join('\n');
|
|
47
|
+
const picked = await ask({
|
|
48
|
+
question: `Found:\n${list}\nInstall into which? [all] `,
|
|
49
|
+
retry: `Type numbers from 1 to ${String(found.length)}, or press Enter for all.`,
|
|
50
|
+
signal,
|
|
51
|
+
}, answer => parseChoice(answer, found.length));
|
|
52
|
+
return picked.map(index => found[index]).filter(agent => agent !== undefined);
|
|
53
|
+
};
|
|
54
|
+
/** An empty answer is a no: the question guards a command that runs code from npm. */
|
|
55
|
+
export const parseConsent = (answer) => {
|
|
56
|
+
const trimmed = answer.trim().toLowerCase();
|
|
57
|
+
if (trimmed === '' || trimmed === 'n' || trimmed === 'no') {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
return trimmed === 'y' || trimmed === 'yes' ? true : undefined;
|
|
61
|
+
};
|
|
62
|
+
export const confirm = async (question, signal) => ask({ question: `${question} [y/N] `, retry: 'Type y or n.', signal }, parseConsent);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { agents, fallback, FALLBACK_COMMAND } from './agents.js';
|
|
2
|
+
const known = [...agents, fallback];
|
|
3
|
+
const nameOf = (result) => known.find(agent => agent.id === result.agent)?.name ?? result.agent;
|
|
4
|
+
export const renderInstall = (results) => {
|
|
5
|
+
const lines = results.map((result) => {
|
|
6
|
+
if (!result.installed) {
|
|
7
|
+
return `✗ ${nameOf(result)}: ${result.error ?? 'not installed'}`;
|
|
8
|
+
}
|
|
9
|
+
const installed = `✓ ${nameOf(result)}: installed. Restart it to load the skills.`;
|
|
10
|
+
return result.warning === undefined ? installed : `${installed}\n Warning: ${result.warning}`;
|
|
11
|
+
});
|
|
12
|
+
if (results.some(result => result.agent === fallback.id)) {
|
|
13
|
+
return lines.join('\n');
|
|
14
|
+
}
|
|
15
|
+
return [...lines, '', `Another agent? Run \`${FALLBACK_COMMAND}\`.`].join('\n');
|
|
16
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '../../cli/commands/skills/install/index.js';
|
|
@@ -149,10 +149,15 @@ export type AsaSearchTermDTO = {
|
|
|
149
149
|
campaign_group_id: string;
|
|
150
150
|
campaign_id: string;
|
|
151
151
|
country_or_region: string;
|
|
152
|
+
impression_midpoint_all: null | string;
|
|
153
|
+
impression_midpoint_first: null | string;
|
|
154
|
+
impression_share_all: null | string;
|
|
155
|
+
impression_share_first: null | string;
|
|
152
156
|
keyword_id: null | string;
|
|
153
157
|
match_type: AsaKeywordMatchType | null;
|
|
154
158
|
metrics: AsaMetricsDTO;
|
|
155
|
-
|
|
159
|
+
rank_all: null | number;
|
|
160
|
+
rank_first: null | number;
|
|
156
161
|
search_popularity: null | number;
|
|
157
162
|
source: null | string;
|
|
158
163
|
text: null | string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type MigrationState = 'running' | 'action_required' | 'completed' | 'failed' | 'canceled';
|
|
2
|
-
export type ActionKind = 'input' | 'upload' | 'external';
|
|
2
|
+
export type ActionKind = 'input' | 'upload' | 'external' | 'agent';
|
|
3
3
|
export type StepStatus = 'locked' | 'active' | 'done';
|
|
4
4
|
export type JsonSchema = Record<string, unknown>;
|
|
5
5
|
export type Envelope<TResult = unknown> = {
|
|
@@ -74,6 +74,9 @@ export type Action = ActionBase & ({
|
|
|
74
74
|
} | {
|
|
75
75
|
kind: 'external';
|
|
76
76
|
href: string;
|
|
77
|
+
} | {
|
|
78
|
+
kind: 'agent';
|
|
79
|
+
input_schema: JsonSchema | null;
|
|
77
80
|
} | {
|
|
78
81
|
kind: string;
|
|
79
82
|
href?: string;
|
package/oclif.manifest.json
CHANGED
|
@@ -421,85 +421,6 @@
|
|
|
421
421
|
"update.js"
|
|
422
422
|
]
|
|
423
423
|
},
|
|
424
|
-
"asa:connect": {
|
|
425
|
-
"aliases": [],
|
|
426
|
-
"args": {},
|
|
427
|
-
"description": "Connect an Apple Search Ads account to this company",
|
|
428
|
-
"examples": [
|
|
429
|
-
"<%= config.bin %> asa connect",
|
|
430
|
-
"<%= config.bin %> asa connect --no-wait"
|
|
431
|
-
],
|
|
432
|
-
"flags": {
|
|
433
|
-
"json": {
|
|
434
|
-
"description": "Format output as json.",
|
|
435
|
-
"helpGroup": "GLOBAL",
|
|
436
|
-
"name": "json",
|
|
437
|
-
"allowNo": false,
|
|
438
|
-
"type": "boolean"
|
|
439
|
-
},
|
|
440
|
-
"timeout": {
|
|
441
|
-
"description": "Seconds to wait for the browser step",
|
|
442
|
-
"name": "timeout",
|
|
443
|
-
"default": 300,
|
|
444
|
-
"hasDynamicHelp": false,
|
|
445
|
-
"multiple": false,
|
|
446
|
-
"type": "option"
|
|
447
|
-
},
|
|
448
|
-
"wait": {
|
|
449
|
-
"description": "Wait until Apple Ads reports as connected",
|
|
450
|
-
"name": "wait",
|
|
451
|
-
"allowNo": true,
|
|
452
|
-
"type": "boolean"
|
|
453
|
-
}
|
|
454
|
-
},
|
|
455
|
-
"hasDynamicHelp": false,
|
|
456
|
-
"hiddenAliases": [],
|
|
457
|
-
"id": "asa:connect",
|
|
458
|
-
"pluginAlias": "adapty",
|
|
459
|
-
"pluginName": "adapty",
|
|
460
|
-
"pluginType": "core",
|
|
461
|
-
"strict": true,
|
|
462
|
-
"enableJsonFlag": true,
|
|
463
|
-
"isESM": true,
|
|
464
|
-
"relativePath": [
|
|
465
|
-
"dist",
|
|
466
|
-
"commands",
|
|
467
|
-
"asa",
|
|
468
|
-
"connect.js"
|
|
469
|
-
]
|
|
470
|
-
},
|
|
471
|
-
"asa:whoami": {
|
|
472
|
-
"aliases": [],
|
|
473
|
-
"args": {},
|
|
474
|
-
"description": "Show which company the token unlocks, whether Apple Ads is connected, and the request budgets it gets\n\nLimits are raised per company, so read them here rather than assuming the defaults: metrics_limit_per_minute\nand metrics_burst_limit govern asa metrics, keywords_read_limit_per_minute the keyword lists,\nmetrics_inflight_limit how many analytics calls may overlap, and max_breakdown_rows_per_page the size a\ngrouped page may project to before it is refused.",
|
|
475
|
-
"examples": [
|
|
476
|
-
"<%= config.bin %> asa whoami"
|
|
477
|
-
],
|
|
478
|
-
"flags": {
|
|
479
|
-
"json": {
|
|
480
|
-
"description": "Format output as json.",
|
|
481
|
-
"helpGroup": "GLOBAL",
|
|
482
|
-
"name": "json",
|
|
483
|
-
"allowNo": false,
|
|
484
|
-
"type": "boolean"
|
|
485
|
-
}
|
|
486
|
-
},
|
|
487
|
-
"hasDynamicHelp": false,
|
|
488
|
-
"hiddenAliases": [],
|
|
489
|
-
"id": "asa:whoami",
|
|
490
|
-
"pluginAlias": "adapty",
|
|
491
|
-
"pluginName": "adapty",
|
|
492
|
-
"pluginType": "core",
|
|
493
|
-
"strict": true,
|
|
494
|
-
"enableJsonFlag": true,
|
|
495
|
-
"isESM": true,
|
|
496
|
-
"relativePath": [
|
|
497
|
-
"dist",
|
|
498
|
-
"commands",
|
|
499
|
-
"asa",
|
|
500
|
-
"whoami.js"
|
|
501
|
-
]
|
|
502
|
-
},
|
|
503
424
|
"attribution:dimensions": {
|
|
504
425
|
"aliases": [],
|
|
505
426
|
"args": {},
|
|
@@ -769,36 +690,13 @@
|
|
|
769
690
|
"values.js"
|
|
770
691
|
]
|
|
771
692
|
},
|
|
772
|
-
"
|
|
773
|
-
"aliases": [],
|
|
774
|
-
"args": {},
|
|
775
|
-
"description": "Authenticate with Adapty via browser",
|
|
776
|
-
"examples": [
|
|
777
|
-
"<%= config.bin %> auth login"
|
|
778
|
-
],
|
|
779
|
-
"flags": {},
|
|
780
|
-
"hasDynamicHelp": false,
|
|
781
|
-
"hiddenAliases": [],
|
|
782
|
-
"id": "auth:login",
|
|
783
|
-
"pluginAlias": "adapty",
|
|
784
|
-
"pluginName": "adapty",
|
|
785
|
-
"pluginType": "core",
|
|
786
|
-
"strict": true,
|
|
787
|
-
"enableJsonFlag": false,
|
|
788
|
-
"isESM": true,
|
|
789
|
-
"relativePath": [
|
|
790
|
-
"dist",
|
|
791
|
-
"commands",
|
|
792
|
-
"auth",
|
|
793
|
-
"login.js"
|
|
794
|
-
]
|
|
795
|
-
},
|
|
796
|
-
"auth:logout": {
|
|
693
|
+
"asa:connect": {
|
|
797
694
|
"aliases": [],
|
|
798
695
|
"args": {},
|
|
799
|
-
"description": "
|
|
696
|
+
"description": "Connect an Apple Search Ads account to this company",
|
|
800
697
|
"examples": [
|
|
801
|
-
"<%= config.bin %>
|
|
698
|
+
"<%= config.bin %> asa connect",
|
|
699
|
+
"<%= config.bin %> asa connect --no-wait"
|
|
802
700
|
],
|
|
803
701
|
"flags": {
|
|
804
702
|
"json": {
|
|
@@ -807,43 +705,25 @@
|
|
|
807
705
|
"name": "json",
|
|
808
706
|
"allowNo": false,
|
|
809
707
|
"type": "boolean"
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
"commands",
|
|
824
|
-
"auth",
|
|
825
|
-
"logout.js"
|
|
826
|
-
]
|
|
827
|
-
},
|
|
828
|
-
"auth:revoke": {
|
|
829
|
-
"aliases": [],
|
|
830
|
-
"args": {},
|
|
831
|
-
"description": "Revoke the current token and remove matching stored credentials and migration selection",
|
|
832
|
-
"examples": [
|
|
833
|
-
"<%= config.bin %> auth revoke"
|
|
834
|
-
],
|
|
835
|
-
"flags": {
|
|
836
|
-
"json": {
|
|
837
|
-
"description": "Format output as json.",
|
|
838
|
-
"helpGroup": "GLOBAL",
|
|
839
|
-
"name": "json",
|
|
840
|
-
"allowNo": false,
|
|
708
|
+
},
|
|
709
|
+
"timeout": {
|
|
710
|
+
"description": "Seconds to wait for the browser step",
|
|
711
|
+
"name": "timeout",
|
|
712
|
+
"default": 300,
|
|
713
|
+
"hasDynamicHelp": false,
|
|
714
|
+
"multiple": false,
|
|
715
|
+
"type": "option"
|
|
716
|
+
},
|
|
717
|
+
"wait": {
|
|
718
|
+
"description": "Wait until Apple Ads reports as connected",
|
|
719
|
+
"name": "wait",
|
|
720
|
+
"allowNo": true,
|
|
841
721
|
"type": "boolean"
|
|
842
722
|
}
|
|
843
723
|
},
|
|
844
724
|
"hasDynamicHelp": false,
|
|
845
725
|
"hiddenAliases": [],
|
|
846
|
-
"id": "
|
|
726
|
+
"id": "asa:connect",
|
|
847
727
|
"pluginAlias": "adapty",
|
|
848
728
|
"pluginName": "adapty",
|
|
849
729
|
"pluginType": "core",
|
|
@@ -853,16 +733,16 @@
|
|
|
853
733
|
"relativePath": [
|
|
854
734
|
"dist",
|
|
855
735
|
"commands",
|
|
856
|
-
"
|
|
857
|
-
"
|
|
736
|
+
"asa",
|
|
737
|
+
"connect.js"
|
|
858
738
|
]
|
|
859
739
|
},
|
|
860
|
-
"
|
|
740
|
+
"asa:whoami": {
|
|
861
741
|
"aliases": [],
|
|
862
742
|
"args": {},
|
|
863
|
-
"description": "Show the
|
|
743
|
+
"description": "Show which company the token unlocks, whether Apple Ads is connected, and the request budgets it gets\n\nLimits are raised per company, so read them here rather than assuming the defaults: metrics_limit_per_minute\nand metrics_burst_limit govern asa metrics, keywords_read_limit_per_minute the keyword lists,\nmetrics_inflight_limit how many analytics calls may overlap, and max_breakdown_rows_per_page the size a\ngrouped page may project to before it is refused.",
|
|
864
744
|
"examples": [
|
|
865
|
-
"<%= config.bin %>
|
|
745
|
+
"<%= config.bin %> asa whoami"
|
|
866
746
|
],
|
|
867
747
|
"flags": {
|
|
868
748
|
"json": {
|
|
@@ -875,7 +755,7 @@
|
|
|
875
755
|
},
|
|
876
756
|
"hasDynamicHelp": false,
|
|
877
757
|
"hiddenAliases": [],
|
|
878
|
-
"id": "
|
|
758
|
+
"id": "asa:whoami",
|
|
879
759
|
"pluginAlias": "adapty",
|
|
880
760
|
"pluginName": "adapty",
|
|
881
761
|
"pluginType": "core",
|
|
@@ -885,38 +765,7 @@
|
|
|
885
765
|
"relativePath": [
|
|
886
766
|
"dist",
|
|
887
767
|
"commands",
|
|
888
|
-
"
|
|
889
|
-
"status.js"
|
|
890
|
-
]
|
|
891
|
-
},
|
|
892
|
-
"auth:whoami": {
|
|
893
|
-
"aliases": [],
|
|
894
|
-
"args": {},
|
|
895
|
-
"description": "Show the current user from the server (verifies the token)",
|
|
896
|
-
"examples": [
|
|
897
|
-
"<%= config.bin %> auth whoami"
|
|
898
|
-
],
|
|
899
|
-
"flags": {
|
|
900
|
-
"json": {
|
|
901
|
-
"description": "Format output as json.",
|
|
902
|
-
"helpGroup": "GLOBAL",
|
|
903
|
-
"name": "json",
|
|
904
|
-
"allowNo": false,
|
|
905
|
-
"type": "boolean"
|
|
906
|
-
}
|
|
907
|
-
},
|
|
908
|
-
"hasDynamicHelp": false,
|
|
909
|
-
"hiddenAliases": [],
|
|
910
|
-
"id": "auth:whoami",
|
|
911
|
-
"pluginAlias": "adapty",
|
|
912
|
-
"pluginName": "adapty",
|
|
913
|
-
"pluginType": "core",
|
|
914
|
-
"strict": true,
|
|
915
|
-
"isESM": true,
|
|
916
|
-
"relativePath": [
|
|
917
|
-
"dist",
|
|
918
|
-
"commands",
|
|
919
|
-
"auth",
|
|
768
|
+
"asa",
|
|
920
769
|
"whoami.js"
|
|
921
770
|
]
|
|
922
771
|
},
|
|
@@ -1422,7 +1271,7 @@
|
|
|
1422
1271
|
"required": true
|
|
1423
1272
|
}
|
|
1424
1273
|
},
|
|
1425
|
-
"description": "Choose an action ID from next_actions or available_actions in `adapty migrations status -m ID --json`.\nReplace ACTION_ID in the examples with an offered action ID.\nFor input actions, read the resources in reads and prepare a JSON object matching input_schema.\nOmitting input sends {}. Review confirm before passing --yes.\nWithout --yes, actions requiring confirmation exit with code 6. No confirmation prompt is shown.\n\nExternal actions print a link and open it in an interactive terminal. Complete the browser step, then check status.\nExternal actions reject --input and --input-file, including stdin.\nPipes and --json require --open to launch a browser; BROWSER=none disables it.\nFile uploads are not supported; use the dashboard or an offered Cloud Export action.\n\nWith --json, returns the full migration response (before the browser step for external actions).\nAfter a revision_conflict error, read status and review the action before retrying.",
|
|
1274
|
+
"description": "Choose an action ID from next_actions or available_actions in `adapty migrations status -m ID --json`.\nReplace ACTION_ID in the examples with an offered action ID.\nFor input actions, read the resources in reads and prepare a JSON object matching input_schema.\nOmitting input sends {}. Review confirm before passing --yes.\nWithout --yes, actions requiring confirmation exit with code 6. No confirmation prompt is shown.\n\nExternal actions print a link and open it in an interactive terminal. Complete the browser step, then check status.\nExternal actions reject --input and --input-file, including stdin.\nPipes and --json require --open to launch a browser; BROWSER=none disables it.\nFile uploads are not supported; use the dashboard or an offered Cloud Export action.\n\nAn agent action writes the resources in reads to .git/adapty/<migration ID>/ in the app's git repository and\nprints what a coding agent needs to do it. Run it from the agent, or paste its output into one. The agent\nreports the result by running the action again with --input.\n\nWith --json, returns the full migration response (before the browser step for external actions). For an agent\naction without --input, returns { action_id, files, instructions }: the files written and the text for the agent.\nAfter a revision_conflict error, read status and review the action before retrying.",
|
|
1426
1275
|
"examples": [
|
|
1427
1276
|
{
|
|
1428
1277
|
"description": "Run an offered action on the saved migration after reviewing confirm:",
|
|
@@ -1521,7 +1370,7 @@
|
|
|
1521
1370
|
"pluginName": "adapty",
|
|
1522
1371
|
"pluginType": "core",
|
|
1523
1372
|
"strict": true,
|
|
1524
|
-
"summary": "Run an input action
|
|
1373
|
+
"summary": "Run an input action, open an external action link, or hand an agent action to a coding agent",
|
|
1525
1374
|
"isESM": true,
|
|
1526
1375
|
"relativePath": [
|
|
1527
1376
|
"dist",
|
|
@@ -2691,6 +2540,210 @@
|
|
|
2691
2540
|
"list.js"
|
|
2692
2541
|
]
|
|
2693
2542
|
},
|
|
2543
|
+
"skills:install": {
|
|
2544
|
+
"aliases": [],
|
|
2545
|
+
"args": {},
|
|
2546
|
+
"description": "Install the Adapty skills into the coding agents on this machine",
|
|
2547
|
+
"examples": [
|
|
2548
|
+
"<%= config.bin %> skills install",
|
|
2549
|
+
"<%= config.bin %> skills install --agent claude-code",
|
|
2550
|
+
"<%= config.bin %> skills install --yes"
|
|
2551
|
+
],
|
|
2552
|
+
"flags": {
|
|
2553
|
+
"json": {
|
|
2554
|
+
"description": "Format output as json.",
|
|
2555
|
+
"helpGroup": "GLOBAL",
|
|
2556
|
+
"name": "json",
|
|
2557
|
+
"allowNo": false,
|
|
2558
|
+
"type": "boolean"
|
|
2559
|
+
},
|
|
2560
|
+
"agent": {
|
|
2561
|
+
"description": "Install into this agent only. Repeat for several.",
|
|
2562
|
+
"name": "agent",
|
|
2563
|
+
"hasDynamicHelp": false,
|
|
2564
|
+
"multiple": true,
|
|
2565
|
+
"options": [
|
|
2566
|
+
"claude-code",
|
|
2567
|
+
"codex",
|
|
2568
|
+
"gemini-cli"
|
|
2569
|
+
],
|
|
2570
|
+
"type": "option"
|
|
2571
|
+
},
|
|
2572
|
+
"yes": {
|
|
2573
|
+
"char": "y",
|
|
2574
|
+
"description": "Install into every agent found without asking; required when several are found and no one can answer. Never runs the npx fallback, which is always asked for",
|
|
2575
|
+
"name": "yes",
|
|
2576
|
+
"allowNo": false,
|
|
2577
|
+
"type": "boolean"
|
|
2578
|
+
}
|
|
2579
|
+
},
|
|
2580
|
+
"hasDynamicHelp": false,
|
|
2581
|
+
"hiddenAliases": [],
|
|
2582
|
+
"id": "skills:install",
|
|
2583
|
+
"pluginAlias": "adapty",
|
|
2584
|
+
"pluginName": "adapty",
|
|
2585
|
+
"pluginType": "core",
|
|
2586
|
+
"strict": true,
|
|
2587
|
+
"enableJsonFlag": true,
|
|
2588
|
+
"isESM": true,
|
|
2589
|
+
"relativePath": [
|
|
2590
|
+
"dist",
|
|
2591
|
+
"commands",
|
|
2592
|
+
"skills",
|
|
2593
|
+
"install.js"
|
|
2594
|
+
]
|
|
2595
|
+
},
|
|
2596
|
+
"auth:login": {
|
|
2597
|
+
"aliases": [],
|
|
2598
|
+
"args": {},
|
|
2599
|
+
"description": "Authenticate with Adapty via browser",
|
|
2600
|
+
"examples": [
|
|
2601
|
+
"<%= config.bin %> auth login"
|
|
2602
|
+
],
|
|
2603
|
+
"flags": {},
|
|
2604
|
+
"hasDynamicHelp": false,
|
|
2605
|
+
"hiddenAliases": [],
|
|
2606
|
+
"id": "auth:login",
|
|
2607
|
+
"pluginAlias": "adapty",
|
|
2608
|
+
"pluginName": "adapty",
|
|
2609
|
+
"pluginType": "core",
|
|
2610
|
+
"strict": true,
|
|
2611
|
+
"enableJsonFlag": false,
|
|
2612
|
+
"isESM": true,
|
|
2613
|
+
"relativePath": [
|
|
2614
|
+
"dist",
|
|
2615
|
+
"commands",
|
|
2616
|
+
"auth",
|
|
2617
|
+
"login.js"
|
|
2618
|
+
]
|
|
2619
|
+
},
|
|
2620
|
+
"auth:logout": {
|
|
2621
|
+
"aliases": [],
|
|
2622
|
+
"args": {},
|
|
2623
|
+
"description": "Remove stored credentials and the saved migration selection",
|
|
2624
|
+
"examples": [
|
|
2625
|
+
"<%= config.bin %> auth logout"
|
|
2626
|
+
],
|
|
2627
|
+
"flags": {
|
|
2628
|
+
"json": {
|
|
2629
|
+
"description": "Format output as json.",
|
|
2630
|
+
"helpGroup": "GLOBAL",
|
|
2631
|
+
"name": "json",
|
|
2632
|
+
"allowNo": false,
|
|
2633
|
+
"type": "boolean"
|
|
2634
|
+
}
|
|
2635
|
+
},
|
|
2636
|
+
"hasDynamicHelp": false,
|
|
2637
|
+
"hiddenAliases": [],
|
|
2638
|
+
"id": "auth:logout",
|
|
2639
|
+
"pluginAlias": "adapty",
|
|
2640
|
+
"pluginName": "adapty",
|
|
2641
|
+
"pluginType": "core",
|
|
2642
|
+
"strict": true,
|
|
2643
|
+
"enableJsonFlag": true,
|
|
2644
|
+
"isESM": true,
|
|
2645
|
+
"relativePath": [
|
|
2646
|
+
"dist",
|
|
2647
|
+
"commands",
|
|
2648
|
+
"auth",
|
|
2649
|
+
"logout.js"
|
|
2650
|
+
]
|
|
2651
|
+
},
|
|
2652
|
+
"auth:revoke": {
|
|
2653
|
+
"aliases": [],
|
|
2654
|
+
"args": {},
|
|
2655
|
+
"description": "Revoke the current token and remove matching stored credentials and migration selection",
|
|
2656
|
+
"examples": [
|
|
2657
|
+
"<%= config.bin %> auth revoke"
|
|
2658
|
+
],
|
|
2659
|
+
"flags": {
|
|
2660
|
+
"json": {
|
|
2661
|
+
"description": "Format output as json.",
|
|
2662
|
+
"helpGroup": "GLOBAL",
|
|
2663
|
+
"name": "json",
|
|
2664
|
+
"allowNo": false,
|
|
2665
|
+
"type": "boolean"
|
|
2666
|
+
}
|
|
2667
|
+
},
|
|
2668
|
+
"hasDynamicHelp": false,
|
|
2669
|
+
"hiddenAliases": [],
|
|
2670
|
+
"id": "auth:revoke",
|
|
2671
|
+
"pluginAlias": "adapty",
|
|
2672
|
+
"pluginName": "adapty",
|
|
2673
|
+
"pluginType": "core",
|
|
2674
|
+
"strict": true,
|
|
2675
|
+
"enableJsonFlag": true,
|
|
2676
|
+
"isESM": true,
|
|
2677
|
+
"relativePath": [
|
|
2678
|
+
"dist",
|
|
2679
|
+
"commands",
|
|
2680
|
+
"auth",
|
|
2681
|
+
"revoke.js"
|
|
2682
|
+
]
|
|
2683
|
+
},
|
|
2684
|
+
"auth:status": {
|
|
2685
|
+
"aliases": [],
|
|
2686
|
+
"args": {},
|
|
2687
|
+
"description": "Show the local authentication state (no network)",
|
|
2688
|
+
"examples": [
|
|
2689
|
+
"<%= config.bin %> auth status"
|
|
2690
|
+
],
|
|
2691
|
+
"flags": {
|
|
2692
|
+
"json": {
|
|
2693
|
+
"description": "Format output as json.",
|
|
2694
|
+
"helpGroup": "GLOBAL",
|
|
2695
|
+
"name": "json",
|
|
2696
|
+
"allowNo": false,
|
|
2697
|
+
"type": "boolean"
|
|
2698
|
+
}
|
|
2699
|
+
},
|
|
2700
|
+
"hasDynamicHelp": false,
|
|
2701
|
+
"hiddenAliases": [],
|
|
2702
|
+
"id": "auth:status",
|
|
2703
|
+
"pluginAlias": "adapty",
|
|
2704
|
+
"pluginName": "adapty",
|
|
2705
|
+
"pluginType": "core",
|
|
2706
|
+
"strict": true,
|
|
2707
|
+
"enableJsonFlag": true,
|
|
2708
|
+
"isESM": true,
|
|
2709
|
+
"relativePath": [
|
|
2710
|
+
"dist",
|
|
2711
|
+
"commands",
|
|
2712
|
+
"auth",
|
|
2713
|
+
"status.js"
|
|
2714
|
+
]
|
|
2715
|
+
},
|
|
2716
|
+
"auth:whoami": {
|
|
2717
|
+
"aliases": [],
|
|
2718
|
+
"args": {},
|
|
2719
|
+
"description": "Show the current user from the server (verifies the token)",
|
|
2720
|
+
"examples": [
|
|
2721
|
+
"<%= config.bin %> auth whoami"
|
|
2722
|
+
],
|
|
2723
|
+
"flags": {
|
|
2724
|
+
"json": {
|
|
2725
|
+
"description": "Format output as json.",
|
|
2726
|
+
"helpGroup": "GLOBAL",
|
|
2727
|
+
"name": "json",
|
|
2728
|
+
"allowNo": false,
|
|
2729
|
+
"type": "boolean"
|
|
2730
|
+
}
|
|
2731
|
+
},
|
|
2732
|
+
"hasDynamicHelp": false,
|
|
2733
|
+
"hiddenAliases": [],
|
|
2734
|
+
"id": "auth:whoami",
|
|
2735
|
+
"pluginAlias": "adapty",
|
|
2736
|
+
"pluginName": "adapty",
|
|
2737
|
+
"pluginType": "core",
|
|
2738
|
+
"strict": true,
|
|
2739
|
+
"isESM": true,
|
|
2740
|
+
"relativePath": [
|
|
2741
|
+
"dist",
|
|
2742
|
+
"commands",
|
|
2743
|
+
"auth",
|
|
2744
|
+
"whoami.js"
|
|
2745
|
+
]
|
|
2746
|
+
},
|
|
2694
2747
|
"asa:ad-groups:create": {
|
|
2695
2748
|
"aliases": [],
|
|
2696
2749
|
"args": {},
|
|
@@ -6104,5 +6157,5 @@
|
|
|
6104
6157
|
]
|
|
6105
6158
|
}
|
|
6106
6159
|
},
|
|
6107
|
-
"version": "0.9.
|
|
6160
|
+
"version": "0.9.3"
|
|
6108
6161
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "adapty",
|
|
3
3
|
"description": "Adapty command line interface",
|
|
4
|
-
"version": "0.9.
|
|
4
|
+
"version": "0.9.3",
|
|
5
5
|
"author": "Adapty team <support@adapty.io>",
|
|
6
6
|
"bin": {
|
|
7
7
|
"adapty": "./bin/run.js"
|
|
@@ -99,6 +99,9 @@
|
|
|
99
99
|
"segments": {
|
|
100
100
|
"description": "List segments"
|
|
101
101
|
},
|
|
102
|
+
"skills": {
|
|
103
|
+
"description": "Install the Adapty skills into coding agents"
|
|
104
|
+
},
|
|
102
105
|
"migrations": {
|
|
103
106
|
"description": "Manage migrations: catalog, transactions and store events"
|
|
104
107
|
},
|