nativeapptemplate-agent 0.1.2 → 0.2.1
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/CLAUDE.md +10 -8
- package/README.md +65 -21
- package/dist/agents/judge.d.ts +8 -0
- package/dist/agents/judge.d.ts.map +1 -1
- package/dist/agents/judge.js +167 -29
- package/dist/agents/judge.js.map +1 -1
- package/dist/agents/repair.d.ts +14 -0
- package/dist/agents/repair.d.ts.map +1 -0
- package/dist/agents/repair.js +103 -0
- package/dist/agents/repair.js.map +1 -0
- package/dist/agents/types.d.ts +34 -1
- package/dist/agents/types.d.ts.map +1 -1
- package/dist/dispatch.d.ts +21 -2
- package/dist/dispatch.d.ts.map +1 -1
- package/dist/dispatch.js +250 -14
- package/dist/dispatch.js.map +1 -1
- package/dist/env-bridge.d.ts +17 -0
- package/dist/env-bridge.d.ts.map +1 -0
- package/dist/env-bridge.js +211 -0
- package/dist/env-bridge.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +88 -3
- package/dist/index.js.map +1 -1
- package/dist/mcp.d.ts +5 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +83 -0
- package/dist/mcp.js.map +1 -0
- package/dist/mobile.d.ts +31 -0
- package/dist/mobile.d.ts.map +1 -0
- package/dist/mobile.js +179 -0
- package/dist/mobile.js.map +1 -0
- package/dist/rails-lifecycle.d.ts +16 -0
- package/dist/rails-lifecycle.d.ts.map +1 -0
- package/dist/rails-lifecycle.js +184 -0
- package/dist/rails-lifecycle.js.map +1 -0
- package/dist/rename-overrides.d.ts +23 -0
- package/dist/rename-overrides.d.ts.map +1 -0
- package/dist/rename-overrides.js +58 -0
- package/dist/rename-overrides.js.map +1 -0
- package/dist/repair-loop.d.ts +25 -0
- package/dist/repair-loop.d.ts.map +1 -0
- package/dist/repair-loop.js +67 -0
- package/dist/repair-loop.js.map +1 -0
- package/dist/report/collect.d.ts +28 -0
- package/dist/report/collect.d.ts.map +1 -0
- package/dist/report/collect.js +117 -0
- package/dist/report/collect.js.map +1 -0
- package/dist/report/model.d.ts +49 -0
- package/dist/report/model.d.ts.map +1 -0
- package/dist/report/model.js +2 -0
- package/dist/report/model.js.map +1 -0
- package/dist/report/render.d.ts +3 -0
- package/dist/report/render.d.ts.map +1 -0
- package/dist/report/render.js +242 -0
- package/dist/report/render.js.map +1 -0
- package/dist/report/theme.d.ts +2 -0
- package/dist/report/theme.d.ts.map +1 -0
- package/dist/report/theme.js +115 -0
- package/dist/report/theme.js.map +1 -0
- package/dist/slug.d.ts +3 -0
- package/dist/slug.d.ts.map +1 -1
- package/dist/slug.js +30 -0
- package/dist/slug.js.map +1 -1
- package/dist/validation/capture.d.ts.map +1 -1
- package/dist/validation/capture.js +19 -2
- package/dist/validation/capture.js.map +1 -1
- package/dist/validation/discover.js +1 -1
- package/dist/validation/discover.js.map +1 -1
- package/dist/validation/index.d.ts +8 -2
- package/dist/validation/index.d.ts.map +1 -1
- package/dist/validation/index.js +4 -1
- package/dist/validation/index.js.map +1 -1
- package/dist/validation/launch.d.ts +10 -0
- package/dist/validation/launch.d.ts.map +1 -1
- package/dist/validation/launch.js +127 -12
- package/dist/validation/launch.js.map +1 -1
- package/dist/validation/layer1.d.ts.map +1 -1
- package/dist/validation/layer1.js +12 -4
- package/dist/validation/layer1.js.map +1 -1
- package/dist/validation/layer2.js +1 -1
- package/dist/validation/layer2.js.map +1 -1
- package/dist/validation/scenarios/queue.d.ts +12 -0
- package/dist/validation/scenarios/queue.d.ts.map +1 -0
- package/dist/validation/scenarios/queue.js +216 -0
- package/dist/validation/scenarios/queue.js.map +1 -0
- package/dist/validation/stage1.d.ts +2 -0
- package/dist/validation/stage1.d.ts.map +1 -1
- package/dist/validation/stage1.js +2 -0
- package/dist/validation/stage1.js.map +1 -1
- package/dist/validation/stage2-judge.d.ts +23 -0
- package/dist/validation/stage2-judge.d.ts.map +1 -0
- package/dist/validation/stage2-judge.js +225 -0
- package/dist/validation/stage2-judge.js.map +1 -0
- package/dist/validation/stage2.d.ts +74 -0
- package/dist/validation/stage2.d.ts.map +1 -0
- package/dist/validation/stage2.js +328 -0
- package/dist/validation/stage2.js.map +1 -0
- package/dist/validation/visual-judge.d.ts +37 -0
- package/dist/validation/visual-judge.d.ts.map +1 -1
- package/dist/validation/visual-judge.js +100 -16
- package/dist/validation/visual-judge.js.map +1 -1
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +17 -0
- package/dist/version.js.map +1 -0
- package/package.json +6 -3
- package/scripts/ruby/rename.rb +7 -3
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { slugToPascal } from "./slug.js";
|
|
5
|
+
// Bridges substrate-named API config (NATIVEAPPTEMPLATE_API_*) to the
|
|
6
|
+
// renamed-product equivalents (<PRODUCT>_API_*) so the agent's auto-
|
|
7
|
+
// validation runs see the right values without forcing the user to
|
|
8
|
+
// hand-set N env vars per generated app.
|
|
9
|
+
//
|
|
10
|
+
// Three sinks the agent has to feed:
|
|
11
|
+
// 1. ~/.gradle/gradle.properties (so Android Studio + manual ./gradlew
|
|
12
|
+
// see the value when the user opens the generated project later)
|
|
13
|
+
// 2. ORG_GRADLE_PROJECT_<KEY>=<value> in the child env when the agent
|
|
14
|
+
// itself spawns ./gradlew (Layer 2 build mode, defensive — works
|
|
15
|
+
// even if gradle.properties is mid-write or the user hasn't saved)
|
|
16
|
+
// 3. SIMCTL_CHILD_<KEY>=<value> in the child env when the agent spawns
|
|
17
|
+
// `xcrun simctl launch` (so the env var reaches the running iOS
|
|
18
|
+
// app via simctl's child-env passthrough convention)
|
|
19
|
+
//
|
|
20
|
+
// Source of truth, by suffix:
|
|
21
|
+
// API_DOMAIN ← $NATIVEAPPTEMPLATE_API/.env HOST
|
|
22
|
+
// API_PORT ← $NATIVEAPPTEMPLATE_API/.env PORT
|
|
23
|
+
// API_SCHEME ← shell env NATIVEAPPTEMPLATE_API_SCHEME, fall back to
|
|
24
|
+
// ~/.gradle/gradle.properties (since the substrate's
|
|
25
|
+
// Rails .env doesn't carry SCHEME)
|
|
26
|
+
//
|
|
27
|
+
// Single source of truth: the substrate's own Rails .env (where Rails
|
|
28
|
+
// actually binds). The mobile clients connect to exactly that host:port,
|
|
29
|
+
// so reading from .env eliminates the drift risk of a parallel shell
|
|
30
|
+
// export. User maintains one file, .env, for their dev-network setup.
|
|
31
|
+
// The three keys we mirror. Adding a new one here automatically threads
|
|
32
|
+
// it through all three sinks. Listed in the same order they appear in
|
|
33
|
+
// the substrate's gradle.properties / .env.sample so diffs read
|
|
34
|
+
// naturally.
|
|
35
|
+
const MIRRORED_SUFFIXES = ["API_DOMAIN", "API_PORT", "API_SCHEME"];
|
|
36
|
+
const SENTINEL_BEGIN = "# BEGIN nativeapptemplate-agent (mirrored API config — do not edit by hand)";
|
|
37
|
+
const SENTINEL_END = "# END nativeapptemplate-agent";
|
|
38
|
+
export function productTokenFor(domain) {
|
|
39
|
+
return slugToPascal(domain.slug).toUpperCase();
|
|
40
|
+
}
|
|
41
|
+
// Read the substrate side of the bridge. Returns only the keys whose
|
|
42
|
+
// source-of-truth carries a non-empty value — silently omits anything
|
|
43
|
+
// missing. Caller decides what to do with empty results.
|
|
44
|
+
export async function readSubstrateApiVars() {
|
|
45
|
+
const result = {};
|
|
46
|
+
// DOMAIN + PORT come from the substrate's Rails .env (HOST + PORT —
|
|
47
|
+
// where Rails actually binds). Mobile clients must connect to that
|
|
48
|
+
// exact host:port, so this is the single source of truth.
|
|
49
|
+
const substratePath = process.env['NATIVEAPPTEMPLATE_API'];
|
|
50
|
+
if (substratePath) {
|
|
51
|
+
const env = await readDotenv(join(substratePath, ".env"));
|
|
52
|
+
if (env['HOST'])
|
|
53
|
+
result['API_DOMAIN'] = env['HOST'];
|
|
54
|
+
if (env['PORT'])
|
|
55
|
+
result['API_PORT'] = env['PORT'];
|
|
56
|
+
}
|
|
57
|
+
// SCHEME isn't in the Rails .env — pull it from the user's shell
|
|
58
|
+
// env, fall back to ~/.gradle/gradle.properties, fall back to nothing
|
|
59
|
+
// (caller treats missing as "use the app's compiled-in default").
|
|
60
|
+
const shellScheme = process.env['NATIVEAPPTEMPLATE_API_SCHEME'];
|
|
61
|
+
if (shellScheme) {
|
|
62
|
+
result['API_SCHEME'] = shellScheme;
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
const gradleScheme = (await readGradleProperties())['NATIVEAPPTEMPLATE_API_SCHEME'];
|
|
66
|
+
if (gradleScheme)
|
|
67
|
+
result['API_SCHEME'] = gradleScheme;
|
|
68
|
+
}
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
async function readDotenv(path) {
|
|
72
|
+
const raw = await readFileOrEmpty(path);
|
|
73
|
+
const out = {};
|
|
74
|
+
for (const line of raw.split("\n")) {
|
|
75
|
+
const trimmed = line.trim();
|
|
76
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
77
|
+
continue;
|
|
78
|
+
const eq = trimmed.indexOf("=");
|
|
79
|
+
if (eq < 1)
|
|
80
|
+
continue;
|
|
81
|
+
const key = trimmed.slice(0, eq).trim();
|
|
82
|
+
const valueRaw = trimmed.slice(eq + 1).trim();
|
|
83
|
+
const value = valueRaw.replace(/^"(.*)"$/, "$1").replace(/^'(.*)'$/, "$1");
|
|
84
|
+
if (key && value)
|
|
85
|
+
out[key] = value;
|
|
86
|
+
}
|
|
87
|
+
return out;
|
|
88
|
+
}
|
|
89
|
+
export async function buildBridgeValues(domain) {
|
|
90
|
+
const product = productTokenFor(domain);
|
|
91
|
+
const substrate = await readSubstrateApiVars();
|
|
92
|
+
const values = {};
|
|
93
|
+
for (const [suffix, value] of Object.entries(substrate)) {
|
|
94
|
+
values[`${product}_${suffix}`] = value;
|
|
95
|
+
}
|
|
96
|
+
return { values };
|
|
97
|
+
}
|
|
98
|
+
// Set ORG_GRADLE_PROJECT_<KEY> and SIMCTL_CHILD_<KEY> entries in the
|
|
99
|
+
// agent's own process.env so any subsequent child spawn that inherits
|
|
100
|
+
// process.env (via env.ts's scrubbedEnv) sees them. Idempotent — calling
|
|
101
|
+
// twice with the same domain is a no-op.
|
|
102
|
+
export function applyBridgeToProcessEnv(bridge) {
|
|
103
|
+
for (const [key, value] of Object.entries(bridge.values)) {
|
|
104
|
+
process.env[`ORG_GRADLE_PROJECT_${key}`] = value;
|
|
105
|
+
process.env[`SIMCTL_CHILD_${key}`] = value;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Write a sentinel block to ~/.gradle/gradle.properties containing the
|
|
109
|
+
// mirrored keys. Replaces an existing block from a prior run; preserves
|
|
110
|
+
// every line outside the block. If gradle.properties doesn't exist,
|
|
111
|
+
// creates it. If the bridge has no values (substrate vars unset),
|
|
112
|
+
// removes the existing block (so stale values from a prior run don't
|
|
113
|
+
// linger) and writes nothing else.
|
|
114
|
+
//
|
|
115
|
+
// Safety knobs (env vars, both default to off):
|
|
116
|
+
// NATIVEAPPTEMPLATE_BRIDGE=off — skip the file write entirely;
|
|
117
|
+
// process.env injection still
|
|
118
|
+
// runs for the agent's own
|
|
119
|
+
// child-spawn paths.
|
|
120
|
+
// NATIVEAPPTEMPLATE_BRIDGE_DRY_RUN=1 — log what would be written
|
|
121
|
+
// instead of writing. Useful for
|
|
122
|
+
// users who want to inspect the
|
|
123
|
+
// bridge's behavior before
|
|
124
|
+
// giving it write access to
|
|
125
|
+
// their user-global gradle.
|
|
126
|
+
export async function syncGradleProperties(bridge) {
|
|
127
|
+
const path = join(homedir(), ".gradle", "gradle.properties");
|
|
128
|
+
if (process.env['NATIVEAPPTEMPLATE_BRIDGE'] === "off") {
|
|
129
|
+
return { path, mode: "skipped", removedStale: false };
|
|
130
|
+
}
|
|
131
|
+
const existing = await readFileOrEmpty(path);
|
|
132
|
+
const stripped = removeSentinelBlock(existing);
|
|
133
|
+
const hasValues = Object.keys(bridge.values).length > 0;
|
|
134
|
+
const removedStale = stripped !== existing && !hasValues;
|
|
135
|
+
let next;
|
|
136
|
+
if (!hasValues) {
|
|
137
|
+
next = stripped;
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
const block = formatSentinelBlock(bridge.values);
|
|
141
|
+
next = ensureTrailingNewline(stripped) + block + "\n";
|
|
142
|
+
}
|
|
143
|
+
const dryRun = process.env['NATIVEAPPTEMPLATE_BRIDGE_DRY_RUN'] === "1";
|
|
144
|
+
if (next === existing) {
|
|
145
|
+
return { path, mode: "noop", removedStale };
|
|
146
|
+
}
|
|
147
|
+
if (dryRun) {
|
|
148
|
+
return { path, mode: "dry-run", removedStale, preview: next };
|
|
149
|
+
}
|
|
150
|
+
await writeFile(path, next);
|
|
151
|
+
return { path, mode: hasValues ? "wrote" : "noop", removedStale };
|
|
152
|
+
}
|
|
153
|
+
async function readGradleProperties() {
|
|
154
|
+
const path = join(homedir(), ".gradle", "gradle.properties");
|
|
155
|
+
const raw = await readFileOrEmpty(path);
|
|
156
|
+
const out = {};
|
|
157
|
+
for (const line of raw.split("\n")) {
|
|
158
|
+
const trimmed = line.trim();
|
|
159
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
160
|
+
continue;
|
|
161
|
+
const eq = trimmed.indexOf("=");
|
|
162
|
+
if (eq < 1)
|
|
163
|
+
continue;
|
|
164
|
+
const key = trimmed.slice(0, eq).trim();
|
|
165
|
+
const value = trimmed.slice(eq + 1).trim();
|
|
166
|
+
if (key && value)
|
|
167
|
+
out[key] = value;
|
|
168
|
+
}
|
|
169
|
+
return out;
|
|
170
|
+
}
|
|
171
|
+
async function readFileOrEmpty(path) {
|
|
172
|
+
try {
|
|
173
|
+
return await readFile(path, "utf8");
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
return "";
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
function removeSentinelBlock(content) {
|
|
180
|
+
const beginIdx = content.indexOf(SENTINEL_BEGIN);
|
|
181
|
+
if (beginIdx === -1)
|
|
182
|
+
return content;
|
|
183
|
+
const endIdx = content.indexOf(SENTINEL_END, beginIdx);
|
|
184
|
+
if (endIdx === -1)
|
|
185
|
+
return content;
|
|
186
|
+
const after = endIdx + SENTINEL_END.length;
|
|
187
|
+
// Also consume the newline after END if present, so removing leaves
|
|
188
|
+
// no orphan blank line.
|
|
189
|
+
const trailing = content[after] === "\n" ? after + 1 : after;
|
|
190
|
+
// And consume one preceding blank-separator newline so we don't
|
|
191
|
+
// accumulate blanks across cycles.
|
|
192
|
+
let before = beginIdx;
|
|
193
|
+
if (before > 0 && content[before - 1] === "\n" && content[before - 2] === "\n") {
|
|
194
|
+
before -= 1;
|
|
195
|
+
}
|
|
196
|
+
return content.slice(0, before) + content.slice(trailing);
|
|
197
|
+
}
|
|
198
|
+
function formatSentinelBlock(values) {
|
|
199
|
+
const lines = [SENTINEL_BEGIN];
|
|
200
|
+
for (const key of Object.keys(values).sort()) {
|
|
201
|
+
lines.push(`${key}=${values[key]}`);
|
|
202
|
+
}
|
|
203
|
+
lines.push(SENTINEL_END);
|
|
204
|
+
return lines.join("\n");
|
|
205
|
+
}
|
|
206
|
+
function ensureTrailingNewline(s) {
|
|
207
|
+
if (s === "")
|
|
208
|
+
return "";
|
|
209
|
+
return s.endsWith("\n") ? s : s + "\n";
|
|
210
|
+
}
|
|
211
|
+
//# sourceMappingURL=env-bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env-bridge.js","sourceRoot":"","sources":["../src/env-bridge.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { type DispatchReportOptions } from "./dispatch.js";
|
|
3
|
+
import type { RenamePair } from "./agents/types.js";
|
|
4
|
+
export type ParsedArgs = {
|
|
5
|
+
spec: string;
|
|
6
|
+
report: DispatchReportOptions;
|
|
7
|
+
open: boolean;
|
|
8
|
+
exitZero: boolean;
|
|
9
|
+
renameOverrides: RenamePair[];
|
|
10
|
+
projectName?: string;
|
|
11
|
+
};
|
|
12
|
+
export declare function parseArgs(argv: readonly string[]): ParsedArgs;
|
|
2
13
|
export declare function main(spec?: string): Promise<void>;
|
|
3
14
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAIA,OAAO,EAAY,KAAK,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAIrE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAIpD,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,qBAAqB,CAAC;IAC9B,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,UAAU,EAAE,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,UAAU,CAmC7D;AAED,wBAAsB,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAoDvD"}
|
package/dist/index.js
CHANGED
|
@@ -1,23 +1,108 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { realpathSync } from "node:fs";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
4
5
|
import { dispatch } from "./dispatch.js";
|
|
5
6
|
import { loadDotenvIfPresent } from "./env.js";
|
|
7
|
+
import { parseRenamePair } from "./rename-overrides.js";
|
|
8
|
+
import { projectNameToSlug, slugToPascal, isValidSlug } from "./slug.js";
|
|
6
9
|
loadDotenvIfPresent();
|
|
10
|
+
export function parseArgs(argv) {
|
|
11
|
+
const specParts = [];
|
|
12
|
+
const report = {};
|
|
13
|
+
const renameOverrides = [];
|
|
14
|
+
let open = false;
|
|
15
|
+
let exitZero = false;
|
|
16
|
+
let projectName;
|
|
17
|
+
for (let i = 0; i < argv.length; i++) {
|
|
18
|
+
const arg = argv[i];
|
|
19
|
+
if (arg === undefined)
|
|
20
|
+
continue;
|
|
21
|
+
if (arg === "--no-report")
|
|
22
|
+
report.enabled = false;
|
|
23
|
+
else if (arg === "--report-open")
|
|
24
|
+
open = true;
|
|
25
|
+
else if (arg === "--exit-zero")
|
|
26
|
+
exitZero = true;
|
|
27
|
+
else if (arg.startsWith("--report-format=")) {
|
|
28
|
+
const value = arg.slice("--report-format=".length);
|
|
29
|
+
if (value === "html" || value === "json" || value === "both")
|
|
30
|
+
report.format = value;
|
|
31
|
+
}
|
|
32
|
+
else if (arg.startsWith("--report-embed=")) {
|
|
33
|
+
report.embed = arg.slice("--report-embed=".length) !== "false";
|
|
34
|
+
}
|
|
35
|
+
else if (arg === "--rename" || arg.startsWith("--rename=")) {
|
|
36
|
+
// Accept both `--rename From=To` (per ROADMAP) and `--rename=From=To`.
|
|
37
|
+
const raw = arg === "--rename" ? argv[++i] : arg.slice("--rename=".length);
|
|
38
|
+
const pair = parseRenamePair(raw);
|
|
39
|
+
if (pair)
|
|
40
|
+
renameOverrides.push(pair);
|
|
41
|
+
else
|
|
42
|
+
console.error(`warning: ignoring malformed --rename "${raw ?? ""}" (expected From=To, e.g. --rename Shop=Clinic)`);
|
|
43
|
+
}
|
|
44
|
+
else if (arg === "--project-name" || arg.startsWith("--project-name=")) {
|
|
45
|
+
const raw = (arg === "--project-name" ? argv[++i] : arg.slice("--project-name=".length))?.trim();
|
|
46
|
+
// Accept any of "Vet Clinic" / "VetClinic" / "vet-clinic" — the slug is
|
|
47
|
+
// derived in dispatch. Reject only if it yields no valid slug at all.
|
|
48
|
+
if (raw && isValidSlug(projectNameToSlug(raw)))
|
|
49
|
+
projectName = raw;
|
|
50
|
+
else
|
|
51
|
+
console.error(`warning: ignoring invalid --project-name "${raw ?? ""}" (e.g. --project-name="Vet Clinic")`);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
specParts.push(arg);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return { spec: specParts.join(" ").trim(), report, open, exitZero, renameOverrides, ...(projectName !== undefined ? { projectName } : {}) };
|
|
58
|
+
}
|
|
7
59
|
export async function main(spec) {
|
|
8
|
-
const
|
|
60
|
+
const parsed = parseArgs(process.argv.slice(2));
|
|
61
|
+
const input = (spec ?? parsed.spec).trim();
|
|
9
62
|
if (!input) {
|
|
10
|
-
console.error('Usage: nativeapptemplate-agent "your spec here"');
|
|
63
|
+
console.error('Usage: nativeapptemplate-agent "your spec here" [--project-name="Vet Clinic"] [--rename From=To]... [--no-report] [--report-format=html|json|both] [--report-embed=true|false] [--report-open] [--exit-zero]');
|
|
11
64
|
process.exitCode = 1;
|
|
12
65
|
return;
|
|
13
66
|
}
|
|
14
67
|
console.log(`nativeapptemplate-agent: received spec: ${input}`);
|
|
15
68
|
console.log('(tail tmp/trace/*.log in a tiled view via scripts/demo-tmux.sh)');
|
|
16
|
-
const result = await dispatch(input
|
|
69
|
+
const result = await dispatch(input, {
|
|
70
|
+
report: parsed.report,
|
|
71
|
+
renameOverrides: parsed.renameOverrides,
|
|
72
|
+
...(parsed.projectName !== undefined ? { projectName: parsed.projectName } : {}),
|
|
73
|
+
});
|
|
74
|
+
if (parsed.projectName !== undefined) {
|
|
75
|
+
const finalSlug = result.report.meta.slug;
|
|
76
|
+
console.log(`project: ${slugToPascal(finalSlug)} (output out/${finalSlug}/)`);
|
|
77
|
+
}
|
|
78
|
+
for (const o of result.renameOverrideOutcomes) {
|
|
79
|
+
if (o.kind === "changed")
|
|
80
|
+
console.log(`override: ${o.from} → ${o.to} (overrode planner's "${o.was}")`);
|
|
81
|
+
else if (o.kind === "noop")
|
|
82
|
+
console.log(`override: ${o.from} → ${o.to} (already the planner's pick)`);
|
|
83
|
+
else {
|
|
84
|
+
const sources = result.report.domain.renamePlan.map((p) => p.from).join(", ");
|
|
85
|
+
console.error(`warning: --rename ${o.from}=${o.to} matched no planned rename — skipped (renamable: ${sources || "none"})`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
17
88
|
console.log('');
|
|
18
89
|
console.log('=== run complete ===');
|
|
19
90
|
console.log(`result: ${result.summary}`);
|
|
20
91
|
console.log(`overall: ${result.overallPass ? 'PASS' : 'FAIL'}`);
|
|
92
|
+
if (result.reportPaths.htmlPath) {
|
|
93
|
+
console.log(`report: file://${result.reportPaths.htmlPath}`);
|
|
94
|
+
if (parsed.open && process.platform === 'darwin') {
|
|
95
|
+
spawn('open', [result.reportPaths.htmlPath], { stdio: 'ignore', detached: true }).unref();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
else if (result.reportPaths.jsonPath) {
|
|
99
|
+
console.log(`report: ${result.reportPaths.jsonPath}`);
|
|
100
|
+
}
|
|
101
|
+
// Non-zero exit on validation failure so CI / shell `&&` chains catch
|
|
102
|
+
// it. --exit-zero opts out (e.g. when you only want the report).
|
|
103
|
+
if (!result.overallPass && !parsed.exitZero) {
|
|
104
|
+
process.exitCode = 1;
|
|
105
|
+
}
|
|
21
106
|
}
|
|
22
107
|
// Entry guard: run main() when this file is the program entry point. Resolve
|
|
23
108
|
// both sides to canonical (symlink-followed) paths so npm bin symlinks (e.g.
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAA8B,MAAM,eAAe,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAGzE,mBAAmB,EAAE,CAAC;AAWtB,MAAM,UAAU,SAAS,CAAC,IAAuB;IAC/C,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,MAAM,MAAM,GAA0B,EAAE,CAAC;IACzC,MAAM,eAAe,GAAiB,EAAE,CAAC;IACzC,IAAI,IAAI,GAAG,KAAK,CAAC;IACjB,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,WAA+B,CAAC;IACpC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,IAAI,GAAG,KAAK,SAAS;YAAE,SAAS;QAChC,IAAI,GAAG,KAAK,aAAa;YAAE,MAAM,CAAC,OAAO,GAAG,KAAK,CAAC;aAC7C,IAAI,GAAG,KAAK,eAAe;YAAE,IAAI,GAAG,IAAI,CAAC;aACzC,IAAI,GAAG,KAAK,aAAa;YAAE,QAAQ,GAAG,IAAI,CAAC;aAC3C,IAAI,GAAG,CAAC,UAAU,CAAC,kBAAkB,CAAC,EAAE,CAAC;YAC5C,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC;YACnD,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,MAAM;gBAAE,MAAM,CAAC,MAAM,GAAG,KAAK,CAAC;QACtF,CAAC;aAAM,IAAI,GAAG,CAAC,UAAU,CAAC,iBAAiB,CAAC,EAAE,CAAC;YAC7C,MAAM,CAAC,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,iBAAiB,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC;QACjE,CAAC;aAAM,IAAI,GAAG,KAAK,UAAU,IAAI,GAAG,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;YAC7D,uEAAuE;YACvE,MAAM,GAAG,GAAG,GAAG,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;YAC3E,MAAM,IAAI,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;YAClC,IAAI,IAAI;gBAAE,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;;gBAChC,OAAO,CAAC,KAAK,CAAC,yCAAyC,GAAG,IAAI,EAAE,iDAAiD,CAAC,CAAC;QAC1H,CAAC;aAAM,IAAI,GAAG,KAAK,gBAAgB,IAAI,GAAG,CAAC,UAAU,CAAC,iBAAiB,CAAC,EAAE,CAAC;YACzE,MAAM,GAAG,GAAG,CAAC,GAAG,KAAK,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;YACjG,wEAAwE;YACxE,sEAAsE;YACtE,IAAI,GAAG,IAAI,WAAW,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC;gBAAE,WAAW,GAAG,GAAG,CAAC;;gBAC7D,OAAO,CAAC,KAAK,CAAC,6CAA6C,GAAG,IAAI,EAAE,sCAAsC,CAAC,CAAC;QACnH,CAAC;aAAM,CAAC;YACN,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,eAAe,EAAE,GAAG,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AAC9I,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,IAAa;IACtC,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAChD,MAAM,KAAK,GAAG,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;IAC3C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CACX,8MAA8M,CAC/M,CAAC;QACF,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,2CAA2C,KAAK,EAAE,CAAC,CAAC;IAChE,OAAO,CAAC,GAAG,CAAC,iEAAiE,CAAC,CAAC;IAE/E,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,KAAK,EAAE;QACnC,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,eAAe,EAAE,MAAM,CAAC,eAAe;QACvC,GAAG,CAAC,MAAM,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACjF,CAAC,CAAC;IAEH,IAAI,MAAM,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;QACrC,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;QAC1C,OAAO,CAAC,GAAG,CAAC,YAAY,YAAY,CAAC,SAAS,CAAC,gBAAgB,SAAS,IAAI,CAAC,CAAC;IAChF,CAAC;IAED,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,sBAAsB,EAAE,CAAC;QAC9C,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS;YAAE,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,EAAE,yBAAyB,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;aAClG,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM;YAAE,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,EAAE,+BAA+B,CAAC,CAAC;aACjG,CAAC;YACJ,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC9E,OAAO,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,EAAE,oDAAoD,OAAO,IAAI,MAAM,GAAG,CAAC,CAAC;QAC7H,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;IACpC,OAAO,CAAC,GAAG,CAAC,WAAW,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IACzC,OAAO,CAAC,GAAG,CAAC,YAAY,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IAChE,IAAI,MAAM,CAAC,WAAW,CAAC,QAAQ,EAAE,CAAC;QAChC,OAAO,CAAC,GAAG,CAAC,kBAAkB,MAAM,CAAC,WAAW,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC7D,IAAI,MAAM,CAAC,IAAI,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YACjD,KAAK,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;QAC5F,CAAC;IACH,CAAC;SAAM,IAAI,MAAM,CAAC,WAAW,CAAC,QAAQ,EAAE,CAAC;QACvC,OAAO,CAAC,GAAG,CAAC,WAAW,MAAM,CAAC,WAAW,CAAC,QAAQ,EAAE,CAAC,CAAC;IACxD,CAAC;IAED,sEAAsE;IACtE,iEAAiE;IACjE,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;QAC5C,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvB,CAAC;AACH,CAAC;AAED,6EAA6E;AAC7E,6EAA6E;AAC7E,+EAA+E;AAC/E,0EAA0E;AAC1E,0EAA0E;AAC1E,+BAA+B;AAC/B,IAAI,YAAY,EAAE,EAAE,CAAC;IACnB,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;QACnB,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACnB,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvB,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,YAAY;IACnB,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC9B,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClD,MAAM,SAAS,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;QACtC,OAAO,SAAS,KAAK,UAAU,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
package/dist/mcp.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":";AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAcpE,wBAAgB,eAAe,IAAI,SAAS,CAsD3C;AAED,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAK1C"}
|
package/dist/mcp.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { realpathSync } from "node:fs";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { dispatch } from "./dispatch.js";
|
|
8
|
+
import { loadDotenvIfPresent } from "./env.js";
|
|
9
|
+
import { readPackageVersion } from "./version.js";
|
|
10
|
+
// MCP surface (MONETIZATION.md §"MCP as a distribution surface"):
|
|
11
|
+
// thin wrapper around dispatch() so any MCP-compatible AI assistant
|
|
12
|
+
// (Claude Code, Cursor, Cline, Continue, Goose, ...) can invoke the
|
|
13
|
+
// agent as a tool. Same backend as the CLI; different wire format.
|
|
14
|
+
//
|
|
15
|
+
// Run via: `npx -y nativeapptemplate-agent-mcp` from an MCP client config.
|
|
16
|
+
export function createMcpServer() {
|
|
17
|
+
const server = new McpServer({
|
|
18
|
+
name: "nativeapptemplate-agent",
|
|
19
|
+
version: readPackageVersion(),
|
|
20
|
+
});
|
|
21
|
+
server.registerTool("generate_app", {
|
|
22
|
+
title: "Generate three-platform SaaS app",
|
|
23
|
+
description: "Generate a working three-platform SaaS app from a natural-language spec — Rails 8.1 API + SwiftUI iOS + Jetpack Compose Android. Validated end-to-end (rename completeness, build, vision judge). Output lands in ./out/<spec-slug>/{rails,ios,android}/. Each invocation runs ~3-5 minutes and consumes Anthropic API usage on the configured key, separate from the calling assistant's session.",
|
|
24
|
+
inputSchema: {
|
|
25
|
+
spec: z
|
|
26
|
+
.string()
|
|
27
|
+
.min(1)
|
|
28
|
+
.describe('Natural-language SaaS spec, e.g. "a walk-in queue for a barbershop"'),
|
|
29
|
+
renameOverrides: z
|
|
30
|
+
.array(z.object({ from: z.string().min(1), to: z.string().min(1) }))
|
|
31
|
+
.optional()
|
|
32
|
+
.describe('Optional manual rename overrides keyed on the substrate token (from = "Shop" | "Shopkeeper" | "ItemTag"), each replacing the planner\'s chosen target. Overrides that match no planned rename are ignored.'),
|
|
33
|
+
projectName: z
|
|
34
|
+
.string()
|
|
35
|
+
.optional()
|
|
36
|
+
.describe('Optional project name (e.g. "Vet Clinic" or "VetClinic"). Overrides the planner\'s name; the slug — which sets the output directory, DB prefix, and the Pascal project name across all three platforms — and the display name are derived from it. Ignored if it yields no valid slug.'),
|
|
37
|
+
},
|
|
38
|
+
}, async ({ spec, renameOverrides, projectName }) => {
|
|
39
|
+
const result = await dispatch(spec, {
|
|
40
|
+
...(renameOverrides ? { renameOverrides } : {}),
|
|
41
|
+
...(projectName !== undefined ? { projectName } : {}),
|
|
42
|
+
});
|
|
43
|
+
return {
|
|
44
|
+
content: [{ type: "text", text: result.summary }],
|
|
45
|
+
structuredContent: {
|
|
46
|
+
overallPass: result.overallPass,
|
|
47
|
+
summary: result.summary,
|
|
48
|
+
...(result.visual ? { visual: result.visual } : {}),
|
|
49
|
+
report: result.report,
|
|
50
|
+
...(result.reportPaths.htmlPath ? { reportHtmlPath: result.reportPaths.htmlPath } : {}),
|
|
51
|
+
...(result.reportPaths.jsonPath ? { reportJsonPath: result.reportPaths.jsonPath } : {}),
|
|
52
|
+
},
|
|
53
|
+
isError: !result.overallPass,
|
|
54
|
+
};
|
|
55
|
+
});
|
|
56
|
+
return server;
|
|
57
|
+
}
|
|
58
|
+
export async function main() {
|
|
59
|
+
loadDotenvIfPresent();
|
|
60
|
+
const server = createMcpServer();
|
|
61
|
+
const transport = new StdioServerTransport();
|
|
62
|
+
await server.connect(transport);
|
|
63
|
+
}
|
|
64
|
+
if (isEntryPoint()) {
|
|
65
|
+
main().catch((err) => {
|
|
66
|
+
console.error(err);
|
|
67
|
+
process.exitCode = 1;
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
function isEntryPoint() {
|
|
71
|
+
const argv1 = process.argv[1];
|
|
72
|
+
if (!argv1)
|
|
73
|
+
return false;
|
|
74
|
+
try {
|
|
75
|
+
const modulePath = fileURLToPath(import.meta.url);
|
|
76
|
+
const argv1Real = realpathSync(argv1);
|
|
77
|
+
return argv1Real === modulePath;
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=mcp.js.map
|
package/dist/mcp.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"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"}
|
package/dist/mobile.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
2
|
+
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
3
|
+
export type MobilePlatform = "ios" | "android";
|
|
4
|
+
export type ScreenElement = {
|
|
5
|
+
readonly [key: string]: unknown;
|
|
6
|
+
};
|
|
7
|
+
export type Screenshot = {
|
|
8
|
+
readonly data: Buffer;
|
|
9
|
+
readonly mimeType: string;
|
|
10
|
+
};
|
|
11
|
+
export type MobileClient = {
|
|
12
|
+
callTool(name: string, args?: Record<string, unknown>): Promise<CallToolResult>;
|
|
13
|
+
useDevice(name: string | undefined): void;
|
|
14
|
+
listDevices(): Promise<readonly ScreenElement[]>;
|
|
15
|
+
listElements(): Promise<readonly ScreenElement[]>;
|
|
16
|
+
click(x: number, y: number): Promise<void>;
|
|
17
|
+
typeKeys(text: string, submit?: boolean): Promise<void>;
|
|
18
|
+
pressButton(button: string): Promise<void>;
|
|
19
|
+
launchApp(packageName: string): Promise<void>;
|
|
20
|
+
takeScreenshot(): Promise<Screenshot>;
|
|
21
|
+
saveScreenshot(absolutePath: string): Promise<void>;
|
|
22
|
+
close(): Promise<void>;
|
|
23
|
+
};
|
|
24
|
+
export type MobileClientOptions = {
|
|
25
|
+
command?: string;
|
|
26
|
+
args?: readonly string[];
|
|
27
|
+
};
|
|
28
|
+
export declare function createMobileClient(opts?: MobileClientOptions): Promise<MobileClient>;
|
|
29
|
+
export declare function attachMobileClient(client: Client): MobileClient;
|
|
30
|
+
export declare function isStubMobile(): boolean;
|
|
31
|
+
//# sourceMappingURL=mobile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mobile.d.ts","sourceRoot":"","sources":["../src/mobile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAEnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAgBzE,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,SAAS,CAAC;AAE/C,MAAM,MAAM,aAAa,GAAG;IAI1B,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IAEvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IAKhF,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC;IAC1C,WAAW,IAAI,OAAO,CAAC,SAAS,aAAa,EAAE,CAAC,CAAC;IACjD,YAAY,IAAI,OAAO,CAAC,SAAS,aAAa,EAAE,CAAC,CAAC;IAClD,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAK3C,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAI3C,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,cAAc,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;IACtC,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAGhC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1B,CAAC;AAmBF,wBAAsB,kBAAkB,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,YAAY,CAAC,CAY1F;AAKD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAE/D;AAED,wBAAgB,YAAY,IAAI,OAAO,CAKtC"}
|