@jskit-ai/jskit-cli 0.2.73 → 0.2.75
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/package.json +4 -4
- package/src/server/cliRuntime/completion.js +40 -4
- package/src/server/cliRuntime/packageIntrospection/placementNormalization.js +19 -5
- package/src/server/commandHandlers/list.js +82 -21
- package/src/server/commandHandlers/mobile.js +95 -138
- package/src/server/commandHandlers/mobileCommandCatalog.js +34 -39
- package/src/server/commandHandlers/mobileShellSupport.js +6 -6
- package/src/server/core/argParser.js +6 -0
- package/src/server/core/buildCommandDeps.js +2 -1
- package/src/server/core/commandCatalog.js +16 -12
- package/src/server/core/createCliRunner.js +6 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/jskit-cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.75",
|
|
4
4
|
"description": "Bundle and package orchestration CLI for JSKIT apps.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
"test": "node --test"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@jskit-ai/jskit-catalog": "0.1.
|
|
24
|
-
"@jskit-ai/kernel": "0.1.
|
|
25
|
-
"@jskit-ai/shell-web": "0.1.
|
|
23
|
+
"@jskit-ai/jskit-catalog": "0.1.74",
|
|
24
|
+
"@jskit-ai/kernel": "0.1.66",
|
|
25
|
+
"@jskit-ai/shell-web": "0.1.65"
|
|
26
26
|
},
|
|
27
27
|
"engines": {
|
|
28
28
|
"node": "20.x"
|
|
@@ -2,6 +2,10 @@ import path from "node:path";
|
|
|
2
2
|
import { pathToFileURL } from "node:url";
|
|
3
3
|
import { access, readdir, readFile } from "node:fs/promises";
|
|
4
4
|
import { buildCrudFieldContractMap } from "@jskit-ai/kernel/shared/support/crudFieldContract";
|
|
5
|
+
import {
|
|
6
|
+
discoverPlacementTopologyFromApp,
|
|
7
|
+
discoverShellOutletTargetsFromApp
|
|
8
|
+
} from "@jskit-ai/kernel/server/support";
|
|
5
9
|
import {
|
|
6
10
|
buildAppCommandOptionMeta,
|
|
7
11
|
listAppCommandDefinitions
|
|
@@ -370,6 +374,37 @@ async function discoverPlacementTargets(appRoot) {
|
|
|
370
374
|
return uniqueSorted(placementValues);
|
|
371
375
|
}
|
|
372
376
|
|
|
377
|
+
async function discoverSemanticPlacementTargets(appRoot) {
|
|
378
|
+
try {
|
|
379
|
+
const topology = await discoverPlacementTopologyFromApp({ appRoot });
|
|
380
|
+
return uniqueSorted(
|
|
381
|
+
(Array.isArray(topology?.placements) ? topology.placements : [])
|
|
382
|
+
.map((placement) => normalizeText(placement?.id))
|
|
383
|
+
.filter(Boolean)
|
|
384
|
+
);
|
|
385
|
+
} catch {
|
|
386
|
+
const topologyPath = path.join(appRoot, "src", "placementTopology.js");
|
|
387
|
+
if (!(await pathExists(topologyPath))) {
|
|
388
|
+
return [];
|
|
389
|
+
}
|
|
390
|
+
const source = await readFile(topologyPath, "utf8");
|
|
391
|
+
return uniqueSorted(extractMatches(source, [/\bid\s*:\s*["']([^"':]+(?:\.[^"':]+)+)["']/g]));
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
async function discoverConcretePlacementTargets(appRoot) {
|
|
396
|
+
try {
|
|
397
|
+
const discovered = await discoverShellOutletTargetsFromApp({ appRoot });
|
|
398
|
+
return uniqueSorted(
|
|
399
|
+
(Array.isArray(discovered?.targets) ? discovered.targets : [])
|
|
400
|
+
.map((target) => normalizeText(target?.id || target?.target))
|
|
401
|
+
.filter(Boolean)
|
|
402
|
+
);
|
|
403
|
+
} catch {
|
|
404
|
+
return discoverPlacementTargets(appRoot);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
373
408
|
async function discoverComponentTokens(appRoot) {
|
|
374
409
|
const tokens = [];
|
|
375
410
|
for (const filePath of [
|
|
@@ -391,7 +426,6 @@ async function discoverComponentTokens(appRoot) {
|
|
|
391
426
|
const source = await readFile(filePath, "utf8");
|
|
392
427
|
tokens.push(...extractMatches(source, [
|
|
393
428
|
/\bcomponentToken\s*:\s*["']([^"']+)["']/g,
|
|
394
|
-
/\bdefault-link-component-token\s*=\s*["']([^"']+)["']/g,
|
|
395
429
|
/registerMainClientComponent\(\s*["']([^"']+)["']/g
|
|
396
430
|
]));
|
|
397
431
|
}
|
|
@@ -590,9 +624,11 @@ async function completeOptionValue({
|
|
|
590
624
|
|
|
591
625
|
if (normalizedOptionName === "resource-file") {
|
|
592
626
|
suggestions = await discoverResourceFiles(appRoot);
|
|
593
|
-
} else if (["link-placement", "placement"
|
|
594
|
-
suggestions = await
|
|
595
|
-
} else if (normalizedOptionName === "
|
|
627
|
+
} else if (["link-placement", "placement"].includes(normalizedOptionName)) {
|
|
628
|
+
suggestions = await discoverSemanticPlacementTargets(appRoot);
|
|
629
|
+
} else if (normalizedOptionName === "target") {
|
|
630
|
+
suggestions = await discoverConcretePlacementTargets(appRoot);
|
|
631
|
+
} else if (normalizedOptionName === "link-renderer") {
|
|
596
632
|
suggestions = await discoverComponentTokens(appRoot);
|
|
597
633
|
} else if (normalizedOptionName === "surface") {
|
|
598
634
|
suggestions = await discoverSurfaces(appRoot);
|
|
@@ -3,7 +3,11 @@ import {
|
|
|
3
3
|
ensureObject
|
|
4
4
|
} from "../../shared/collectionUtils.js";
|
|
5
5
|
import {
|
|
6
|
-
|
|
6
|
+
normalizePlacementKind,
|
|
7
|
+
normalizePlacementOwnerId,
|
|
8
|
+
normalizePlacementTopologyDefinition,
|
|
9
|
+
normalizeShellOutletTargetId,
|
|
10
|
+
resolvePlacementTargetReference
|
|
7
11
|
} from "@jskit-ai/kernel/shared/support/shellLayoutTargets";
|
|
8
12
|
|
|
9
13
|
function normalizePlacementOutlets(value) {
|
|
@@ -41,22 +45,27 @@ function normalizePlacementContributions(value) {
|
|
|
41
45
|
for (const entry of ensureArray(value)) {
|
|
42
46
|
const record = ensureObject(entry);
|
|
43
47
|
const id = String(record.id || "").trim();
|
|
44
|
-
const
|
|
45
|
-
if (!id || !
|
|
48
|
+
const targetReference = resolvePlacementTargetReference(record.target);
|
|
49
|
+
if (!id || !targetReference?.id) {
|
|
46
50
|
continue;
|
|
47
51
|
}
|
|
48
52
|
|
|
49
53
|
const surfaces = [...new Set(ensureArray(record.surfaces).map((item) => String(item || "").trim()).filter(Boolean))];
|
|
54
|
+
const kind = normalizePlacementKind(record.kind) || (String(record.componentToken || "").trim() ? "component" : "link");
|
|
50
55
|
const componentToken = String(record.componentToken || "").trim();
|
|
51
56
|
const when = String(record.when || "").trim();
|
|
52
57
|
const description = String(record.description || "").trim();
|
|
53
58
|
const source = String(record.source || "").trim();
|
|
59
|
+
const owner = normalizePlacementOwnerId(record.owner);
|
|
54
60
|
const parsedOrder = Number(record.order);
|
|
55
61
|
const order = Number.isFinite(parsedOrder) ? Math.trunc(parsedOrder) : null;
|
|
56
62
|
contributions.push(
|
|
57
63
|
Object.freeze({
|
|
58
64
|
id,
|
|
59
|
-
target,
|
|
65
|
+
target: targetReference.id,
|
|
66
|
+
targetType: targetReference.type,
|
|
67
|
+
owner,
|
|
68
|
+
kind,
|
|
60
69
|
surfaces: Object.freeze(surfaces),
|
|
61
70
|
order,
|
|
62
71
|
componentToken,
|
|
@@ -83,7 +92,12 @@ function normalizePlacementContributions(value) {
|
|
|
83
92
|
);
|
|
84
93
|
}
|
|
85
94
|
|
|
95
|
+
function normalizePlacementTopology(value, { context = "package placement topology" } = {}) {
|
|
96
|
+
return normalizePlacementTopologyDefinition(value, { context }).placements;
|
|
97
|
+
}
|
|
98
|
+
|
|
86
99
|
export {
|
|
87
100
|
normalizePlacementContributions,
|
|
88
|
-
normalizePlacementOutlets
|
|
101
|
+
normalizePlacementOutlets,
|
|
102
|
+
normalizePlacementTopology
|
|
89
103
|
};
|
|
@@ -177,6 +177,7 @@ function createListCommands(ctx = {}) {
|
|
|
177
177
|
loadBundleRegistry,
|
|
178
178
|
loadAppLocalPackageRegistry,
|
|
179
179
|
resolveInstalledNodeModulePackageEntry,
|
|
180
|
+
discoverPlacementTopologyFromApp,
|
|
180
181
|
discoverShellOutletTargetsFromApp,
|
|
181
182
|
normalizePlacementContributions,
|
|
182
183
|
resolvePackageKind
|
|
@@ -435,39 +436,99 @@ function createListCommands(ctx = {}) {
|
|
|
435
436
|
|
|
436
437
|
async function commandListPlacements({ options, cwd, stdout }) {
|
|
437
438
|
const appRoot = await resolveAppRootFromCwd(cwd);
|
|
438
|
-
const
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
});
|
|
442
|
-
const
|
|
439
|
+
const showConcreteOnly = options.concrete === true && options.all !== true;
|
|
440
|
+
const showConcrete = options.concrete === true || options.all === true;
|
|
441
|
+
const showSemantic = showConcreteOnly !== true;
|
|
442
|
+
const discoveredTopology = await discoverPlacementTopologyFromApp({ appRoot });
|
|
443
|
+
const semanticPlacements = ensureArray(discoveredTopology.placements)
|
|
444
|
+
.map((entry) => ensureObject(entry))
|
|
445
|
+
.filter((entry) => String(entry.id || "").trim())
|
|
446
|
+
.sort((left, right) => {
|
|
447
|
+
const idCompare = String(left.id || "").localeCompare(String(right.id || ""));
|
|
448
|
+
if (idCompare !== 0) {
|
|
449
|
+
return idCompare;
|
|
450
|
+
}
|
|
451
|
+
return String(left.owner || "").localeCompare(String(right.owner || ""));
|
|
452
|
+
});
|
|
453
|
+
const discoveredConcrete = showConcrete
|
|
454
|
+
? await discoverShellOutletTargetsFromApp({
|
|
455
|
+
appRoot,
|
|
456
|
+
sourceRoot: "src"
|
|
457
|
+
})
|
|
458
|
+
: { targets: [] };
|
|
459
|
+
const concreteTargets = ensureArray(discoveredConcrete.targets)
|
|
443
460
|
.map((entry) => ensureObject(entry))
|
|
444
461
|
.filter((entry) => String(entry.id || "").trim())
|
|
445
462
|
.sort((left, right) => String(left.id || "").localeCompare(String(right.id || "")));
|
|
446
463
|
|
|
447
464
|
if (options.json) {
|
|
448
465
|
const payload = {
|
|
449
|
-
placements:
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
466
|
+
placements: showSemantic
|
|
467
|
+
? semanticPlacements.map((placementTarget) => ({
|
|
468
|
+
target: String(placementTarget.id || "").trim(),
|
|
469
|
+
owner: String(placementTarget.owner || "").trim(),
|
|
470
|
+
default: placementTarget.default === true,
|
|
471
|
+
description: String(placementTarget.description || "").trim(),
|
|
472
|
+
surfaces: ensureArray(placementTarget.surfaces).map((entry) => String(entry || "").trim()).filter(Boolean),
|
|
473
|
+
variants: ensureObject(placementTarget.variants),
|
|
474
|
+
sourcePath: String(placementTarget.sourcePath || "").trim()
|
|
475
|
+
}))
|
|
476
|
+
: [],
|
|
477
|
+
concretePlacements: showConcrete
|
|
478
|
+
? concreteTargets.map((placementTarget) => ({
|
|
479
|
+
target: String(placementTarget.id || "").trim(),
|
|
480
|
+
default: placementTarget.default === true,
|
|
481
|
+
sourcePath: String(placementTarget.sourcePath || "").trim()
|
|
482
|
+
}))
|
|
483
|
+
: []
|
|
454
484
|
};
|
|
455
485
|
stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
456
486
|
return 0;
|
|
457
487
|
}
|
|
458
488
|
|
|
459
489
|
const color = createColorFormatter(stdout);
|
|
460
|
-
const lines = [
|
|
461
|
-
if (
|
|
462
|
-
lines.push("
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
const
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
490
|
+
const lines = [];
|
|
491
|
+
if (showSemantic) {
|
|
492
|
+
lines.push(color.heading("Available placements:"));
|
|
493
|
+
if (semanticPlacements.length < 1) {
|
|
494
|
+
lines.push("- none");
|
|
495
|
+
} else {
|
|
496
|
+
for (const placementTarget of semanticPlacements) {
|
|
497
|
+
const placementId = String(placementTarget.id || "").trim();
|
|
498
|
+
const owner = String(placementTarget.owner || "").trim();
|
|
499
|
+
const ownerLabel = owner ? color.dim(` [owner:${owner}]`) : "";
|
|
500
|
+
const defaultLabel = placementTarget.default === true ? color.installed(" (default)") : "";
|
|
501
|
+
const description = String(placementTarget.description || "").trim();
|
|
502
|
+
const descriptionSuffix = description ? `: ${description}` : "";
|
|
503
|
+
lines.push(`- ${color.item(placementId)}${ownerLabel}${defaultLabel}${descriptionSuffix}`);
|
|
504
|
+
const variants = ensureObject(placementTarget.variants);
|
|
505
|
+
for (const layoutClass of ["compact", "medium", "expanded"]) {
|
|
506
|
+
const variant = ensureObject(variants[layoutClass]);
|
|
507
|
+
const outlet = String(variant.outlet || "").trim();
|
|
508
|
+
if (outlet) {
|
|
509
|
+
lines.push(` ${layoutClass} -> ${color.dim(outlet)}`);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
if (showConcrete) {
|
|
517
|
+
if (lines.length > 0) {
|
|
518
|
+
lines.push("");
|
|
519
|
+
}
|
|
520
|
+
lines.push(color.heading("Available concrete outlets:"));
|
|
521
|
+
if (concreteTargets.length < 1) {
|
|
522
|
+
lines.push("- none");
|
|
523
|
+
} else {
|
|
524
|
+
for (const placementTarget of concreteTargets) {
|
|
525
|
+
const placementId = String(placementTarget.id || "").trim();
|
|
526
|
+
const sourcePath = String(placementTarget.sourcePath || "").trim();
|
|
527
|
+
const isDefault = placementTarget.default === true;
|
|
528
|
+
const defaultLabel = isDefault ? color.installed(" (default)") : "";
|
|
529
|
+
const sourceLabel = sourcePath ? ` ${color.dim(`[${sourcePath}]`)}` : "";
|
|
530
|
+
lines.push(`- ${color.item(placementId)}${defaultLabel}${sourceLabel}`);
|
|
531
|
+
}
|
|
471
532
|
}
|
|
472
533
|
}
|
|
473
534
|
|
|
@@ -60,7 +60,7 @@ async function collectManagedMobileFileDriftIssues({
|
|
|
60
60
|
}
|
|
61
61
|
if (currentContent !== expectedContent) {
|
|
62
62
|
issues.push(
|
|
63
|
-
`${normalizeRelativePath(appRoot, absolutePath)} is stale and no longer matches config.mobile. Re-run jskit mobile sync
|
|
63
|
+
`${normalizeRelativePath(appRoot, absolutePath)} is stale and no longer matches config.mobile. Re-run jskit mobile android sync to refresh managed mobile-shell files.`
|
|
64
64
|
);
|
|
65
65
|
}
|
|
66
66
|
}
|
|
@@ -106,24 +106,53 @@ async function collectMissingInstalledDependencyNames(ctx, appRoot = "", package
|
|
|
106
106
|
return missing;
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
function
|
|
109
|
+
function renderAndroidMobileCommandList(lines, color) {
|
|
110
|
+
for (const entry of listMobileCommandDefinitions()) {
|
|
111
|
+
if (entry.name === "dev") {
|
|
112
|
+
lines.push(
|
|
113
|
+
` - ${color.item(entry.name)}: Shortcut to run ${color.emphasis("sync")}, ${color.emphasis("tunnel")}, ${color.emphasis("run")} in this order`
|
|
114
|
+
);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
lines.push(` - ${color.item(entry.name)}: ${entry.summary}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function renderMobileHelp(stream, definition = null, platform = "") {
|
|
110
122
|
const color = createColorFormatter(stream);
|
|
111
123
|
const lines = [];
|
|
112
124
|
|
|
113
|
-
if (!definition) {
|
|
125
|
+
if (!definition && !platform) {
|
|
114
126
|
lines.push(`Command: ${color.emphasis("mobile")}`);
|
|
115
127
|
lines.push("");
|
|
116
128
|
lines.push(color.heading("1) Minimal use"));
|
|
117
|
-
lines.push(" jskit mobile <subcommand>");
|
|
129
|
+
lines.push(" jskit mobile <platform> <subcommand>");
|
|
118
130
|
lines.push("");
|
|
119
|
-
lines.push(color.heading("2)
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
131
|
+
lines.push(color.heading("2) Platforms"));
|
|
132
|
+
lines.push(` - ${color.item("android")}`);
|
|
133
|
+
renderAndroidMobileCommandList(lines, color);
|
|
123
134
|
lines.push("");
|
|
124
135
|
lines.push(color.heading("3) Notes"));
|
|
125
136
|
lines.push(" - Mobile helpers are for the Stage 1 Android Capacitor shell flow.");
|
|
126
|
-
lines.push(" - Use jskit mobile <
|
|
137
|
+
lines.push(" - Use jskit mobile <platform> help for platform-specific usage.");
|
|
138
|
+
writeWrappedLines({
|
|
139
|
+
stdout: stream,
|
|
140
|
+
lines
|
|
141
|
+
});
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (!definition) {
|
|
146
|
+
lines.push(`Mobile platform: ${color.emphasis(platform)}`);
|
|
147
|
+
lines.push("");
|
|
148
|
+
lines.push(color.heading("1) Minimal use"));
|
|
149
|
+
lines.push(` jskit mobile ${platform} <subcommand>`);
|
|
150
|
+
lines.push("");
|
|
151
|
+
lines.push(color.heading("2) Subcommands"));
|
|
152
|
+
renderAndroidMobileCommandList(lines, color);
|
|
153
|
+
lines.push("");
|
|
154
|
+
lines.push(color.heading("3) Notes"));
|
|
155
|
+
lines.push(` - Use jskit mobile ${platform} <subcommand> help for subcommand-specific usage.`);
|
|
127
156
|
writeWrappedLines({
|
|
128
157
|
stdout: stream,
|
|
129
158
|
lines
|
|
@@ -256,7 +285,7 @@ function resolveAdbReversePort({
|
|
|
256
285
|
const port = Number(parsedUrl.port || "");
|
|
257
286
|
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
258
287
|
throw createCliError(
|
|
259
|
-
`config.mobile.apiBaseUrl "${apiBaseUrl}" must include an explicit port so jskit mobile tunnel
|
|
288
|
+
`config.mobile.apiBaseUrl "${apiBaseUrl}" must include an explicit port so jskit mobile android tunnel can infer adb reverse.`
|
|
260
289
|
);
|
|
261
290
|
}
|
|
262
291
|
|
|
@@ -337,7 +366,7 @@ async function resolveAndroidDeviceTarget({
|
|
|
337
366
|
appRoot
|
|
338
367
|
});
|
|
339
368
|
if (devices.length < 1) {
|
|
340
|
-
throw ctx.createCliError(`No Android devices are visible to adb. Run jskit mobile devices
|
|
369
|
+
throw ctx.createCliError(`No Android devices are visible to adb. Run jskit mobile android devices before ${commandLabel}.`);
|
|
341
370
|
}
|
|
342
371
|
|
|
343
372
|
const normalizedExplicitTarget = String(explicitTarget || "").trim();
|
|
@@ -346,7 +375,7 @@ async function resolveAndroidDeviceTarget({
|
|
|
346
375
|
: devices[0];
|
|
347
376
|
|
|
348
377
|
if (!selectedDevice) {
|
|
349
|
-
throw ctx.createCliError(`Android device "${normalizedExplicitTarget}" is not visible to adb. Run jskit mobile devices
|
|
378
|
+
throw ctx.createCliError(`Android device "${normalizedExplicitTarget}" is not visible to adb. Run jskit mobile android devices first.`);
|
|
350
379
|
}
|
|
351
380
|
if (selectedDevice.state !== "device") {
|
|
352
381
|
throw ctx.createCliError(`Android device "${selectedDevice.serial}" is currently "${selectedDevice.state}", not ready for ${commandLabel}.`);
|
|
@@ -402,7 +431,7 @@ async function runLocalBinary(binaryName, args = [], {
|
|
|
402
431
|
if (error?.code === "ENOENT") {
|
|
403
432
|
reject(
|
|
404
433
|
createCliError(
|
|
405
|
-
`Could not find local "${binaryName}" in node_modules/.bin. Re-run jskit
|
|
434
|
+
`Could not find local "${binaryName}" in node_modules/.bin. Re-run jskit add package @jskit-ai/mobile-capacitor after npm install succeeds.`
|
|
406
435
|
)
|
|
407
436
|
);
|
|
408
437
|
return;
|
|
@@ -513,28 +542,6 @@ async function refreshManagedMobileFiles({
|
|
|
513
542
|
}
|
|
514
543
|
}
|
|
515
544
|
|
|
516
|
-
async function runMobileAddCapacitorCommand({
|
|
517
|
-
commandAdd,
|
|
518
|
-
appRoot,
|
|
519
|
-
options = {},
|
|
520
|
-
stdout,
|
|
521
|
-
stderr
|
|
522
|
-
}) {
|
|
523
|
-
return await commandAdd({
|
|
524
|
-
positional: ["package", CAPACITOR_RUNTIME_PACKAGE_ID],
|
|
525
|
-
options: {
|
|
526
|
-
...options,
|
|
527
|
-
runNpmInstall: true,
|
|
528
|
-
inlineOptions: {}
|
|
529
|
-
},
|
|
530
|
-
cwd: appRoot,
|
|
531
|
-
io: {
|
|
532
|
-
stdout,
|
|
533
|
-
stderr
|
|
534
|
-
}
|
|
535
|
-
});
|
|
536
|
-
}
|
|
537
|
-
|
|
538
545
|
async function runMobileSyncAndroidCommand({
|
|
539
546
|
ctx,
|
|
540
547
|
commandAdd,
|
|
@@ -738,7 +745,7 @@ async function runMobileBuildAndroidCommand({
|
|
|
738
745
|
|
|
739
746
|
if (mobileConfig.assetMode !== "bundled") {
|
|
740
747
|
throw createCliError(
|
|
741
|
-
'jskit mobile build
|
|
748
|
+
'jskit mobile android build requires config.mobile.assetMode="bundled" so the release shell does not depend on a live dev server.'
|
|
742
749
|
);
|
|
743
750
|
}
|
|
744
751
|
|
|
@@ -906,9 +913,6 @@ async function runMobileTunnelAndroidCommand({
|
|
|
906
913
|
}) {
|
|
907
914
|
const inlineOptions = normalizeInlineOptions(options);
|
|
908
915
|
const target = String(inlineOptions.target || "").trim();
|
|
909
|
-
if (!target) {
|
|
910
|
-
throw ctx.createCliError("jskit mobile tunnel android requires --target <device-id>.");
|
|
911
|
-
}
|
|
912
916
|
|
|
913
917
|
const mobileConfig = await resolveInstalledMobileConfigForCommand({
|
|
914
918
|
appRoot,
|
|
@@ -960,9 +964,6 @@ async function runMobileRestartAndroidCommand({
|
|
|
960
964
|
}) {
|
|
961
965
|
const inlineOptions = normalizeInlineOptions(options);
|
|
962
966
|
const target = String(inlineOptions.target || "").trim();
|
|
963
|
-
if (!target) {
|
|
964
|
-
throw ctx.createCliError("jskit mobile restart android requires --target <device-id>.");
|
|
965
|
-
}
|
|
966
967
|
|
|
967
968
|
const mobileConfig = await resolveInstalledMobileConfigForCommand({
|
|
968
969
|
appRoot,
|
|
@@ -1020,7 +1021,7 @@ async function runMobileDevAndroidCommand({
|
|
|
1020
1021
|
|
|
1021
1022
|
stdout.write(`[mobile] Using Android device: ${selectedDevice.serial}\n`);
|
|
1022
1023
|
stdout.write("[mobile] Building and syncing the Android shell:\n");
|
|
1023
|
-
stdout.write("[mobile] npx jskit mobile sync
|
|
1024
|
+
stdout.write("[mobile] npx jskit mobile android sync\n");
|
|
1024
1025
|
await runMobileSyncAndroidCommand({
|
|
1025
1026
|
ctx,
|
|
1026
1027
|
commandAdd,
|
|
@@ -1030,20 +1031,8 @@ async function runMobileDevAndroidCommand({
|
|
|
1030
1031
|
stderr
|
|
1031
1032
|
});
|
|
1032
1033
|
|
|
1033
|
-
stdout.write(`[mobile] Installing and launching the app on ${selectedDevice.serial}:\n`);
|
|
1034
|
-
stdout.write(`[mobile] npx jskit mobile run android --target ${selectedDevice.serial}\n`);
|
|
1035
|
-
await runCapRunAndroidCommand({
|
|
1036
|
-
ctx,
|
|
1037
|
-
appRoot,
|
|
1038
|
-
pathModule: ctx.path,
|
|
1039
|
-
target: selectedDevice.serial,
|
|
1040
|
-
stdout,
|
|
1041
|
-
stderr,
|
|
1042
|
-
dryRun: false
|
|
1043
|
-
});
|
|
1044
|
-
|
|
1045
1034
|
stdout.write(`[mobile] Creating the adb reverse tunnel on ${selectedDevice.serial}:\n`);
|
|
1046
|
-
stdout.write(`[mobile] npx jskit mobile tunnel
|
|
1035
|
+
stdout.write(`[mobile] npx jskit mobile android tunnel --target ${selectedDevice.serial}\n`);
|
|
1047
1036
|
await runMobileTunnelAndroidCommand({
|
|
1048
1037
|
ctx,
|
|
1049
1038
|
appRoot,
|
|
@@ -1056,6 +1045,18 @@ async function runMobileDevAndroidCommand({
|
|
|
1056
1045
|
stderr
|
|
1057
1046
|
});
|
|
1058
1047
|
|
|
1048
|
+
stdout.write(`[mobile] Installing and launching the app on ${selectedDevice.serial}:\n`);
|
|
1049
|
+
stdout.write(`[mobile] npx jskit mobile android run --target ${selectedDevice.serial}\n`);
|
|
1050
|
+
await runCapRunAndroidCommand({
|
|
1051
|
+
ctx,
|
|
1052
|
+
appRoot,
|
|
1053
|
+
pathModule: ctx.path,
|
|
1054
|
+
target: selectedDevice.serial,
|
|
1055
|
+
stdout,
|
|
1056
|
+
stderr,
|
|
1057
|
+
dryRun: false
|
|
1058
|
+
});
|
|
1059
|
+
|
|
1059
1060
|
return 0;
|
|
1060
1061
|
}
|
|
1061
1062
|
|
|
@@ -1072,7 +1073,8 @@ function createMobileCommands(ctx = {}, { commandAdd } = {}) {
|
|
|
1072
1073
|
async function commandMobile({ positional = [], options = {}, cwd = "", stdout, stderr }) {
|
|
1073
1074
|
const firstToken = String(positional[0] || "").trim();
|
|
1074
1075
|
const secondToken = String(positional[1] || "").trim();
|
|
1075
|
-
const
|
|
1076
|
+
const thirdToken = String(positional[2] || "").trim();
|
|
1077
|
+
const remainingPositionals = positional.slice(3);
|
|
1076
1078
|
|
|
1077
1079
|
if (!firstToken) {
|
|
1078
1080
|
renderMobileHelp(stdout);
|
|
@@ -1080,19 +1082,31 @@ function createMobileCommands(ctx = {}, { commandAdd } = {}) {
|
|
|
1080
1082
|
}
|
|
1081
1083
|
|
|
1082
1084
|
if (firstToken === "help") {
|
|
1083
|
-
renderMobileHelp(stdout
|
|
1085
|
+
renderMobileHelp(stdout);
|
|
1084
1086
|
return 0;
|
|
1085
1087
|
}
|
|
1086
1088
|
|
|
1087
|
-
const
|
|
1088
|
-
if (
|
|
1089
|
-
throw createCliError(`Unknown mobile
|
|
1089
|
+
const platform = firstToken;
|
|
1090
|
+
if (platform !== "android") {
|
|
1091
|
+
throw createCliError(`Unknown mobile platform: ${platform}.`, {
|
|
1090
1092
|
renderUsage: () => renderMobileHelp(stderr)
|
|
1091
1093
|
});
|
|
1092
1094
|
}
|
|
1093
1095
|
|
|
1094
|
-
if (secondToken === "help") {
|
|
1095
|
-
renderMobileHelp(stdout,
|
|
1096
|
+
if (!secondToken || secondToken === "help") {
|
|
1097
|
+
renderMobileHelp(stdout, null, platform);
|
|
1098
|
+
return 0;
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
const definition = resolveMobileCommandDefinition(secondToken);
|
|
1102
|
+
if (!definition) {
|
|
1103
|
+
throw createCliError(`Unknown mobile ${platform} subcommand: ${secondToken}.`, {
|
|
1104
|
+
renderUsage: () => renderMobileHelp(stderr, null, platform)
|
|
1105
|
+
});
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
if (thirdToken === "help") {
|
|
1109
|
+
renderMobileHelp(stdout, definition, platform);
|
|
1096
1110
|
return 0;
|
|
1097
1111
|
}
|
|
1098
1112
|
|
|
@@ -1102,14 +1116,14 @@ function createMobileCommands(ctx = {}, { commandAdd } = {}) {
|
|
|
1102
1116
|
const unknownInlineOptionNames = inlineOptionNames.filter((optionName) => !supportedOptionNames.has(optionName));
|
|
1103
1117
|
if (unknownInlineOptionNames.length > 0) {
|
|
1104
1118
|
throw createCliError(
|
|
1105
|
-
`Unknown option${unknownInlineOptionNames.length === 1 ? "" : "s"} for jskit mobile ${definition.name}: ${unknownInlineOptionNames.map((optionName) => `--${optionName}`).join(", ")}.`,
|
|
1119
|
+
`Unknown option${unknownInlineOptionNames.length === 1 ? "" : "s"} for jskit mobile ${platform} ${definition.name}: ${unknownInlineOptionNames.map((optionName) => `--${optionName}`).join(", ")}.`,
|
|
1106
1120
|
{
|
|
1107
1121
|
renderUsage: () => renderMobileHelp(stderr, definition)
|
|
1108
1122
|
}
|
|
1109
1123
|
);
|
|
1110
1124
|
}
|
|
1111
1125
|
if (options?.dryRun === true && !supportedOptionNames.has("dry-run")) {
|
|
1112
|
-
throw createCliError(`Unknown option for jskit mobile ${definition.name}: --dry-run.`, {
|
|
1126
|
+
throw createCliError(`Unknown option for jskit mobile ${platform} ${definition.name}: --dry-run.`, {
|
|
1113
1127
|
renderUsage: () => renderMobileHelp(stderr, definition)
|
|
1114
1128
|
});
|
|
1115
1129
|
}
|
|
@@ -1117,13 +1131,8 @@ function createMobileCommands(ctx = {}, { commandAdd } = {}) {
|
|
|
1117
1131
|
const appRoot = await resolveAppRootFromCwd(cwd);
|
|
1118
1132
|
|
|
1119
1133
|
if (definition.name === "devices") {
|
|
1120
|
-
if (
|
|
1121
|
-
throw createCliError(`jskit mobile devices
|
|
1122
|
-
renderUsage: () => renderMobileHelp(stderr, definition)
|
|
1123
|
-
});
|
|
1124
|
-
}
|
|
1125
|
-
if (remainingPositionals.length > 0) {
|
|
1126
|
-
throw createCliError(`Unexpected positional arguments for jskit mobile devices: ${remainingPositionals.join(" ")}`, {
|
|
1134
|
+
if (thirdToken || remainingPositionals.length > 0) {
|
|
1135
|
+
throw createCliError(`Unexpected positional arguments for jskit mobile ${platform} devices: ${[thirdToken, ...remainingPositionals].filter(Boolean).join(" ")}`, {
|
|
1127
1136
|
renderUsage: () => renderMobileHelp(stderr, definition)
|
|
1128
1137
|
});
|
|
1129
1138
|
}
|
|
@@ -1137,13 +1146,8 @@ function createMobileCommands(ctx = {}, { commandAdd } = {}) {
|
|
|
1137
1146
|
}
|
|
1138
1147
|
|
|
1139
1148
|
if (definition.name === "dev") {
|
|
1140
|
-
if (
|
|
1141
|
-
throw createCliError(`jskit mobile dev
|
|
1142
|
-
renderUsage: () => renderMobileHelp(stderr, definition)
|
|
1143
|
-
});
|
|
1144
|
-
}
|
|
1145
|
-
if (remainingPositionals.length > 0) {
|
|
1146
|
-
throw createCliError(`Unexpected positional arguments for jskit mobile dev: ${remainingPositionals.join(" ")}`, {
|
|
1149
|
+
if (thirdToken || remainingPositionals.length > 0) {
|
|
1150
|
+
throw createCliError(`Unexpected positional arguments for jskit mobile ${platform} dev: ${[thirdToken, ...remainingPositionals].filter(Boolean).join(" ")}`, {
|
|
1147
1151
|
renderUsage: () => renderMobileHelp(stderr, definition)
|
|
1148
1152
|
});
|
|
1149
1153
|
}
|
|
@@ -1159,13 +1163,8 @@ function createMobileCommands(ctx = {}, { commandAdd } = {}) {
|
|
|
1159
1163
|
}
|
|
1160
1164
|
|
|
1161
1165
|
if (definition.name === "tunnel") {
|
|
1162
|
-
if (
|
|
1163
|
-
throw createCliError(`jskit mobile tunnel
|
|
1164
|
-
renderUsage: () => renderMobileHelp(stderr, definition)
|
|
1165
|
-
});
|
|
1166
|
-
}
|
|
1167
|
-
if (remainingPositionals.length > 0) {
|
|
1168
|
-
throw createCliError(`Unexpected positional arguments for jskit mobile tunnel: ${remainingPositionals.join(" ")}`, {
|
|
1166
|
+
if (thirdToken || remainingPositionals.length > 0) {
|
|
1167
|
+
throw createCliError(`Unexpected positional arguments for jskit mobile ${platform} tunnel: ${[thirdToken, ...remainingPositionals].filter(Boolean).join(" ")}`, {
|
|
1169
1168
|
renderUsage: () => renderMobileHelp(stderr, definition)
|
|
1170
1169
|
});
|
|
1171
1170
|
}
|
|
@@ -1180,13 +1179,8 @@ function createMobileCommands(ctx = {}, { commandAdd } = {}) {
|
|
|
1180
1179
|
}
|
|
1181
1180
|
|
|
1182
1181
|
if (definition.name === "restart") {
|
|
1183
|
-
if (
|
|
1184
|
-
throw createCliError(`jskit mobile restart
|
|
1185
|
-
renderUsage: () => renderMobileHelp(stderr, definition)
|
|
1186
|
-
});
|
|
1187
|
-
}
|
|
1188
|
-
if (remainingPositionals.length > 0) {
|
|
1189
|
-
throw createCliError(`Unexpected positional arguments for jskit mobile restart: ${remainingPositionals.join(" ")}`, {
|
|
1182
|
+
if (thirdToken || remainingPositionals.length > 0) {
|
|
1183
|
+
throw createCliError(`Unexpected positional arguments for jskit mobile ${platform} restart: ${[thirdToken, ...remainingPositionals].filter(Boolean).join(" ")}`, {
|
|
1190
1184
|
renderUsage: () => renderMobileHelp(stderr, definition)
|
|
1191
1185
|
});
|
|
1192
1186
|
}
|
|
@@ -1200,36 +1194,9 @@ function createMobileCommands(ctx = {}, { commandAdd } = {}) {
|
|
|
1200
1194
|
});
|
|
1201
1195
|
}
|
|
1202
1196
|
|
|
1203
|
-
if (definition.name === "add") {
|
|
1204
|
-
if (secondToken !== "capacitor") {
|
|
1205
|
-
throw createCliError(`jskit mobile add currently supports only "capacitor".`, {
|
|
1206
|
-
renderUsage: () => renderMobileHelp(stderr, definition)
|
|
1207
|
-
});
|
|
1208
|
-
}
|
|
1209
|
-
if (remainingPositionals.length > 0) {
|
|
1210
|
-
throw createCliError(`Unexpected positional arguments for jskit mobile add: ${remainingPositionals.join(" ")}`, {
|
|
1211
|
-
renderUsage: () => renderMobileHelp(stderr, definition)
|
|
1212
|
-
});
|
|
1213
|
-
}
|
|
1214
|
-
|
|
1215
|
-
return runMobileAddCapacitorCommand({
|
|
1216
|
-
ctx,
|
|
1217
|
-
commandAdd,
|
|
1218
|
-
appRoot,
|
|
1219
|
-
options,
|
|
1220
|
-
stdout,
|
|
1221
|
-
stderr
|
|
1222
|
-
});
|
|
1223
|
-
}
|
|
1224
|
-
|
|
1225
1197
|
if (definition.name === "sync") {
|
|
1226
|
-
if (
|
|
1227
|
-
throw createCliError(`jskit mobile sync
|
|
1228
|
-
renderUsage: () => renderMobileHelp(stderr, definition)
|
|
1229
|
-
});
|
|
1230
|
-
}
|
|
1231
|
-
if (remainingPositionals.length > 0) {
|
|
1232
|
-
throw createCliError(`Unexpected positional arguments for jskit mobile sync: ${remainingPositionals.join(" ")}`, {
|
|
1198
|
+
if (thirdToken || remainingPositionals.length > 0) {
|
|
1199
|
+
throw createCliError(`Unexpected positional arguments for jskit mobile ${platform} sync: ${[thirdToken, ...remainingPositionals].filter(Boolean).join(" ")}`, {
|
|
1233
1200
|
renderUsage: () => renderMobileHelp(stderr, definition)
|
|
1234
1201
|
});
|
|
1235
1202
|
}
|
|
@@ -1245,13 +1212,8 @@ function createMobileCommands(ctx = {}, { commandAdd } = {}) {
|
|
|
1245
1212
|
}
|
|
1246
1213
|
|
|
1247
1214
|
if (definition.name === "run") {
|
|
1248
|
-
if (
|
|
1249
|
-
throw createCliError(`jskit mobile run
|
|
1250
|
-
renderUsage: () => renderMobileHelp(stderr, definition)
|
|
1251
|
-
});
|
|
1252
|
-
}
|
|
1253
|
-
if (remainingPositionals.length > 0) {
|
|
1254
|
-
throw createCliError(`Unexpected positional arguments for jskit mobile run: ${remainingPositionals.join(" ")}`, {
|
|
1215
|
+
if (thirdToken || remainingPositionals.length > 0) {
|
|
1216
|
+
throw createCliError(`Unexpected positional arguments for jskit mobile ${platform} run: ${[thirdToken, ...remainingPositionals].filter(Boolean).join(" ")}`, {
|
|
1255
1217
|
renderUsage: () => renderMobileHelp(stderr, definition)
|
|
1256
1218
|
});
|
|
1257
1219
|
}
|
|
@@ -1267,13 +1229,8 @@ function createMobileCommands(ctx = {}, { commandAdd } = {}) {
|
|
|
1267
1229
|
}
|
|
1268
1230
|
|
|
1269
1231
|
if (definition.name === "build") {
|
|
1270
|
-
if (
|
|
1271
|
-
throw createCliError(`jskit mobile build
|
|
1272
|
-
renderUsage: () => renderMobileHelp(stderr, definition)
|
|
1273
|
-
});
|
|
1274
|
-
}
|
|
1275
|
-
if (remainingPositionals.length > 0) {
|
|
1276
|
-
throw createCliError(`Unexpected positional arguments for jskit mobile build: ${remainingPositionals.join(" ")}`, {
|
|
1232
|
+
if (thirdToken || remainingPositionals.length > 0) {
|
|
1233
|
+
throw createCliError(`Unexpected positional arguments for jskit mobile ${platform} build: ${[thirdToken, ...remainingPositionals].filter(Boolean).join(" ")}`, {
|
|
1277
1234
|
renderUsage: () => renderMobileHelp(stderr, definition)
|
|
1278
1235
|
});
|
|
1279
1236
|
}
|
|
@@ -1289,8 +1246,8 @@ function createMobileCommands(ctx = {}, { commandAdd } = {}) {
|
|
|
1289
1246
|
}
|
|
1290
1247
|
|
|
1291
1248
|
if (definition.name === "doctor") {
|
|
1292
|
-
if (
|
|
1293
|
-
throw createCliError(`Unexpected positional arguments for jskit mobile doctor: ${[
|
|
1249
|
+
if (thirdToken || remainingPositionals.length > 0) {
|
|
1250
|
+
throw createCliError(`Unexpected positional arguments for jskit mobile ${platform} doctor: ${[thirdToken, ...remainingPositionals].filter(Boolean).join(" ")}`, {
|
|
1294
1251
|
renderUsage: () => renderMobileHelp(stderr, definition)
|
|
1295
1252
|
});
|
|
1296
1253
|
}
|
|
@@ -1,30 +1,8 @@
|
|
|
1
1
|
const MOBILE_COMMAND_DEFINITIONS = Object.freeze({
|
|
2
|
-
add: Object.freeze({
|
|
3
|
-
name: "add",
|
|
4
|
-
summary: "Install Capacitor mobile-shell support into the current app.",
|
|
5
|
-
usage: "jskit mobile add capacitor [--dry-run] [--devlinks]",
|
|
6
|
-
options: Object.freeze([
|
|
7
|
-
Object.freeze({
|
|
8
|
-
label: "--devlinks",
|
|
9
|
-
description: "Run npm run --if-present devlinks after install for local development relinking."
|
|
10
|
-
}),
|
|
11
|
-
Object.freeze({
|
|
12
|
-
label: "--dry-run",
|
|
13
|
-
description: "Preview the package install and generated files without mutating package.json, lockfiles, or app files."
|
|
14
|
-
})
|
|
15
|
-
]),
|
|
16
|
-
defaults: Object.freeze([
|
|
17
|
-
"Installs @jskit-ai/mobile-capacitor plus the required Capacitor packages.",
|
|
18
|
-
"Renders capacitor.config.json and .jskit/mobile-capacitor.md from config.mobile.",
|
|
19
|
-
"Runs npm install and then cap add android unless --dry-run is used.",
|
|
20
|
-
"Use --devlinks only when you want npm run devlinks to relink the app after install.",
|
|
21
|
-
"If android/ already exists, the Capacitor CLI add step is skipped."
|
|
22
|
-
])
|
|
23
|
-
}),
|
|
24
2
|
sync: Object.freeze({
|
|
25
3
|
name: "sync",
|
|
26
4
|
summary: "Build the JSKIT web client and sync the Android Capacitor shell.",
|
|
27
|
-
usage: "jskit mobile sync
|
|
5
|
+
usage: "jskit mobile android sync [--dry-run] [--devlinks]",
|
|
28
6
|
options: Object.freeze([
|
|
29
7
|
Object.freeze({
|
|
30
8
|
label: "--devlinks",
|
|
@@ -38,13 +16,13 @@ const MOBILE_COMMAND_DEFINITIONS = Object.freeze({
|
|
|
38
16
|
defaults: Object.freeze([
|
|
39
17
|
"Runs npm run build so dist/ matches the current JSKIT web client.",
|
|
40
18
|
"Runs cap sync android after the frontend build succeeds.",
|
|
41
|
-
"Requires capacitor.config.json and android/ from jskit
|
|
19
|
+
"Requires capacitor.config.json and android/ from jskit add package @jskit-ai/mobile-capacitor."
|
|
42
20
|
])
|
|
43
21
|
}),
|
|
44
22
|
run: Object.freeze({
|
|
45
23
|
name: "run",
|
|
46
24
|
summary: "Launch the Android Capacitor shell for the current app.",
|
|
47
|
-
usage: "jskit mobile run
|
|
25
|
+
usage: "jskit mobile android run [--target <device-id>] [--dry-run]",
|
|
48
26
|
options: Object.freeze([
|
|
49
27
|
Object.freeze({
|
|
50
28
|
label: "--target <device-id>",
|
|
@@ -62,8 +40,8 @@ const MOBILE_COMMAND_DEFINITIONS = Object.freeze({
|
|
|
62
40
|
}),
|
|
63
41
|
dev: Object.freeze({
|
|
64
42
|
name: "dev",
|
|
65
|
-
summary: "
|
|
66
|
-
usage: "jskit mobile dev
|
|
43
|
+
summary: "Shortcut to run sync, tunnel, run in this order.",
|
|
44
|
+
usage: "jskit mobile android dev [--target <device-id>]",
|
|
67
45
|
options: Object.freeze([
|
|
68
46
|
Object.freeze({
|
|
69
47
|
label: "--target <device-id>",
|
|
@@ -71,15 +49,15 @@ const MOBILE_COMMAND_DEFINITIONS = Object.freeze({
|
|
|
71
49
|
})
|
|
72
50
|
]),
|
|
73
51
|
defaults: Object.freeze([
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"
|
|
52
|
+
"jskit mobile android sync",
|
|
53
|
+
"jskit mobile android tunnel",
|
|
54
|
+
"jskit mobile android run"
|
|
77
55
|
])
|
|
78
56
|
}),
|
|
79
57
|
devices: Object.freeze({
|
|
80
58
|
name: "devices",
|
|
81
59
|
summary: "List Android devices currently visible to adb.",
|
|
82
|
-
usage: "jskit mobile devices
|
|
60
|
+
usage: "jskit mobile android devices",
|
|
83
61
|
options: Object.freeze([]),
|
|
84
62
|
defaults: Object.freeze([
|
|
85
63
|
"Runs adb devices -l and prints the currently connected Android targets."
|
|
@@ -88,11 +66,11 @@ const MOBILE_COMMAND_DEFINITIONS = Object.freeze({
|
|
|
88
66
|
tunnel: Object.freeze({
|
|
89
67
|
name: "tunnel",
|
|
90
68
|
summary: "Create and verify an adb reverse tunnel for local Android testing.",
|
|
91
|
-
usage: "jskit mobile tunnel
|
|
69
|
+
usage: "jskit mobile android tunnel [--target <device-id>] [--port <port>]",
|
|
92
70
|
options: Object.freeze([
|
|
93
71
|
Object.freeze({
|
|
94
72
|
label: "--target <device-id>",
|
|
95
|
-
description: "
|
|
73
|
+
description: "Optional adb device serial. If omitted, uses the first device from adb devices -l."
|
|
96
74
|
}),
|
|
97
75
|
Object.freeze({
|
|
98
76
|
label: "--port <port>",
|
|
@@ -107,11 +85,11 @@ const MOBILE_COMMAND_DEFINITIONS = Object.freeze({
|
|
|
107
85
|
restart: Object.freeze({
|
|
108
86
|
name: "restart",
|
|
109
87
|
summary: "Clear app data and cold-start the Android shell on a chosen device.",
|
|
110
|
-
usage: "jskit mobile restart
|
|
88
|
+
usage: "jskit mobile android restart [--target <device-id>]",
|
|
111
89
|
options: Object.freeze([
|
|
112
90
|
Object.freeze({
|
|
113
91
|
label: "--target <device-id>",
|
|
114
|
-
description: "
|
|
92
|
+
description: "Optional adb device serial. If omitted, uses the first device from adb devices -l."
|
|
115
93
|
})
|
|
116
94
|
]),
|
|
117
95
|
defaults: Object.freeze([
|
|
@@ -122,7 +100,7 @@ const MOBILE_COMMAND_DEFINITIONS = Object.freeze({
|
|
|
122
100
|
build: Object.freeze({
|
|
123
101
|
name: "build",
|
|
124
102
|
summary: "Build a release Android App Bundle for the current app.",
|
|
125
|
-
usage: "jskit mobile build
|
|
103
|
+
usage: "jskit mobile android build [--dry-run]",
|
|
126
104
|
options: Object.freeze([
|
|
127
105
|
Object.freeze({
|
|
128
106
|
label: "--dry-run",
|
|
@@ -137,7 +115,7 @@ const MOBILE_COMMAND_DEFINITIONS = Object.freeze({
|
|
|
137
115
|
doctor: Object.freeze({
|
|
138
116
|
name: "doctor",
|
|
139
117
|
summary: "Validate the Android Capacitor shell wiring for the current app.",
|
|
140
|
-
usage: "jskit mobile doctor",
|
|
118
|
+
usage: "jskit mobile android doctor",
|
|
141
119
|
options: Object.freeze([]),
|
|
142
120
|
defaults: Object.freeze([
|
|
143
121
|
"Checks config.mobile, capacitor.config.json, android/, and the managed AndroidManifest deep-link filter."
|
|
@@ -146,8 +124,25 @@ const MOBILE_COMMAND_DEFINITIONS = Object.freeze({
|
|
|
146
124
|
});
|
|
147
125
|
|
|
148
126
|
function listMobileCommandDefinitions() {
|
|
127
|
+
const order = new Map([
|
|
128
|
+
["dev", 0],
|
|
129
|
+
["sync", 1],
|
|
130
|
+
["tunnel", 2],
|
|
131
|
+
["run", 3],
|
|
132
|
+
["restart", 4],
|
|
133
|
+
["build", 5],
|
|
134
|
+
["devices", 6],
|
|
135
|
+
["doctor", 7]
|
|
136
|
+
]);
|
|
149
137
|
return Object.values(MOBILE_COMMAND_DEFINITIONS)
|
|
150
|
-
.sort((left, right) =>
|
|
138
|
+
.sort((left, right) => {
|
|
139
|
+
const leftOrder = order.get(left.name) ?? Number.MAX_SAFE_INTEGER;
|
|
140
|
+
const rightOrder = order.get(right.name) ?? Number.MAX_SAFE_INTEGER;
|
|
141
|
+
if (leftOrder !== rightOrder) {
|
|
142
|
+
return leftOrder - rightOrder;
|
|
143
|
+
}
|
|
144
|
+
return left.name.localeCompare(right.name);
|
|
145
|
+
});
|
|
151
146
|
}
|
|
152
147
|
|
|
153
148
|
function resolveMobileCommandDefinition(rawName = "") {
|
|
@@ -168,7 +163,7 @@ function buildMobileCommandOptionMeta(subcommandName = "") {
|
|
|
168
163
|
return optionMeta;
|
|
169
164
|
}
|
|
170
165
|
|
|
171
|
-
if (definition.name === "
|
|
166
|
+
if (definition.name === "sync" || definition.name === "run" || definition.name === "build") {
|
|
172
167
|
optionMeta["dry-run"] = { inputType: "flag" };
|
|
173
168
|
}
|
|
174
169
|
if (definition.name === "run") {
|
|
@@ -619,7 +619,7 @@ async function assertCapacitorShellInstalled({ ctx, appRoot }) {
|
|
|
619
619
|
|
|
620
620
|
if (missingPaths.length > 0) {
|
|
621
621
|
throw ctx.createCliError(
|
|
622
|
-
`Capacitor Android shell is not installed for this app. Missing: ${missingPaths.join(", ")}. Run jskit
|
|
622
|
+
`Capacitor Android shell is not installed for this app. Missing: ${missingPaths.join(", ")}. Run jskit add package @jskit-ai/mobile-capacitor first.`
|
|
623
623
|
);
|
|
624
624
|
}
|
|
625
625
|
}
|
|
@@ -678,7 +678,7 @@ async function ensureAndroidManifestDeepLinks({
|
|
|
678
678
|
const manifestPath = pathModule.join(appRoot, ANDROID_MANIFEST_RELATIVE_PATH);
|
|
679
679
|
if (!(await fileExists(manifestPath))) {
|
|
680
680
|
throw createCliError(
|
|
681
|
-
`Capacitor Android shell is missing ${normalizeRelativePath(appRoot, manifestPath)}. Run jskit
|
|
681
|
+
`Capacitor Android shell is missing ${normalizeRelativePath(appRoot, manifestPath)}. Run jskit add package @jskit-ai/mobile-capacitor first.`
|
|
682
682
|
);
|
|
683
683
|
}
|
|
684
684
|
|
|
@@ -722,7 +722,7 @@ async function collectAndroidNativeShellIdentityIssues({ ctx, appRoot } = {}) {
|
|
|
722
722
|
const expectedSource = renderer(currentSource, nativeConfig);
|
|
723
723
|
if (currentSource !== expectedSource) {
|
|
724
724
|
issues.push(
|
|
725
|
-
`${normalizeRelativePath(appRoot, absolutePath)} is stale and no longer matches config.mobile. Re-run jskit mobile sync
|
|
725
|
+
`${normalizeRelativePath(appRoot, absolutePath)} is stale and no longer matches config.mobile. Re-run jskit mobile android sync to refresh the Android shell.`
|
|
726
726
|
);
|
|
727
727
|
}
|
|
728
728
|
};
|
|
@@ -757,7 +757,7 @@ async function collectAndroidNativeShellIdentityIssues({ ctx, appRoot } = {}) {
|
|
|
757
757
|
currentMainActivitySource !== expectedMainActivitySource
|
|
758
758
|
) {
|
|
759
759
|
issues.push(
|
|
760
|
-
`${normalizeRelativePath(appRoot, mainActivityEntry.absolutePath)} is stale and no longer matches config.mobile. Re-run jskit mobile sync
|
|
760
|
+
`${normalizeRelativePath(appRoot, mainActivityEntry.absolutePath)} is stale and no longer matches config.mobile. Re-run jskit mobile android sync to refresh the Android shell.`
|
|
761
761
|
);
|
|
762
762
|
}
|
|
763
763
|
|
|
@@ -783,7 +783,7 @@ async function ensureAndroidNativeShellIdentity({
|
|
|
783
783
|
const absolutePath = pathModule.join(appRoot, relativePath);
|
|
784
784
|
if (!(await fileExists(absolutePath))) {
|
|
785
785
|
throw createCliError(
|
|
786
|
-
`Capacitor Android shell is missing ${normalizeRelativePath(appRoot, absolutePath)}. Run jskit
|
|
786
|
+
`Capacitor Android shell is missing ${normalizeRelativePath(appRoot, absolutePath)}. Run jskit add package @jskit-ai/mobile-capacitor first.`
|
|
787
787
|
);
|
|
788
788
|
}
|
|
789
789
|
const currentSource = await readFile(absolutePath, "utf8");
|
|
@@ -807,7 +807,7 @@ async function ensureAndroidNativeShellIdentity({
|
|
|
807
807
|
|
|
808
808
|
const mainActivityEntry = await resolveAndroidMainActivityEntry(appRoot);
|
|
809
809
|
if (!mainActivityEntry) {
|
|
810
|
-
throw createCliError("Capacitor Android shell is missing MainActivity.java or MainActivity.kt. Run jskit
|
|
810
|
+
throw createCliError("Capacitor Android shell is missing MainActivity.java or MainActivity.kt. Run jskit add package @jskit-ai/mobile-capacitor first.");
|
|
811
811
|
}
|
|
812
812
|
|
|
813
813
|
const currentMainActivitySource = await readFile(mainActivityEntry.absolutePath, "utf8");
|
|
@@ -23,6 +23,7 @@ function parseArgs(argv, { createCliError } = {}) {
|
|
|
23
23
|
verbose: false,
|
|
24
24
|
json: false,
|
|
25
25
|
all: false,
|
|
26
|
+
concrete: false,
|
|
26
27
|
help: true,
|
|
27
28
|
inlineOptions: {}
|
|
28
29
|
},
|
|
@@ -49,6 +50,7 @@ function parseArgs(argv, { createCliError } = {}) {
|
|
|
49
50
|
verbose: false,
|
|
50
51
|
json: false,
|
|
51
52
|
all: false,
|
|
53
|
+
concrete: false,
|
|
52
54
|
help: false,
|
|
53
55
|
inlineOptions: {}
|
|
54
56
|
};
|
|
@@ -107,6 +109,10 @@ function parseArgs(argv, { createCliError } = {}) {
|
|
|
107
109
|
options.all = true;
|
|
108
110
|
continue;
|
|
109
111
|
}
|
|
112
|
+
if (token === "--concrete") {
|
|
113
|
+
options.concrete = true;
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
110
116
|
if (token === "--help" || token === "-h") {
|
|
111
117
|
options.help = true;
|
|
112
118
|
continue;
|
|
@@ -54,7 +54,8 @@ function createCommandHandlerDeps(deps = {}) {
|
|
|
54
54
|
removeManagedViteProxyEntries: deps.removeManagedViteProxyEntries,
|
|
55
55
|
hashBuffer: deps.hashBuffer,
|
|
56
56
|
rm: deps.rm,
|
|
57
|
-
discoverShellOutletTargetsFromApp: deps.discoverShellOutletTargetsFromApp
|
|
57
|
+
discoverShellOutletTargetsFromApp: deps.discoverShellOutletTargetsFromApp,
|
|
58
|
+
discoverPlacementTopologyFromApp: deps.discoverPlacementTopologyFromApp
|
|
58
59
|
};
|
|
59
60
|
}
|
|
60
61
|
|
|
@@ -173,20 +173,24 @@ const COMMAND_DESCRIPTORS = Object.freeze({
|
|
|
173
173
|
aliases: Object.freeze([]),
|
|
174
174
|
showInOverview: true,
|
|
175
175
|
summary: "Run JSKIT-managed mobile-shell helpers.",
|
|
176
|
-
minimalUse: "jskit mobile
|
|
176
|
+
minimalUse: "jskit mobile android dev",
|
|
177
177
|
parameters: Object.freeze([
|
|
178
|
+
Object.freeze({
|
|
179
|
+
name: "<platform>",
|
|
180
|
+
description: "Currently only android is supported."
|
|
181
|
+
}),
|
|
178
182
|
Object.freeze({
|
|
179
183
|
name: "<subcommand>",
|
|
180
|
-
description: "
|
|
184
|
+
description: "dev | devices | sync | tunnel | restart | run | build | doctor."
|
|
181
185
|
})
|
|
182
186
|
]),
|
|
183
187
|
defaults: Object.freeze([
|
|
184
|
-
"
|
|
185
|
-
"Use jskit mobile <
|
|
186
|
-
"--dry-run is accepted by jskit mobile
|
|
187
|
-
"--devlinks runs npm run --if-present devlinks after
|
|
188
|
+
"Install the shell first with jskit add package @jskit-ai/mobile-capacitor.",
|
|
189
|
+
"Use jskit mobile <platform> help for platform-specific usage.",
|
|
190
|
+
"--dry-run is accepted by jskit mobile android sync/run/build.",
|
|
191
|
+
"--devlinks runs npm run --if-present devlinks after jskit mobile android sync maintenance for development-only relinking."
|
|
188
192
|
]),
|
|
189
|
-
fullUse: "jskit mobile <subcommand> [help] [--dry-run] [--<option> <value>...]",
|
|
193
|
+
fullUse: "jskit mobile <platform> <subcommand> [help] [--dry-run] [--<option> <value>...]",
|
|
190
194
|
showHelpOnBareInvocation: true,
|
|
191
195
|
handlerName: "commandMobile",
|
|
192
196
|
allowedFlagKeys: Object.freeze(["dryRun", "devlinks"]),
|
|
@@ -313,14 +317,14 @@ const COMMAND_DESCRIPTORS = Object.freeze({
|
|
|
313
317
|
minimalUse: "jskit list-placements",
|
|
314
318
|
parameters: Object.freeze([]),
|
|
315
319
|
defaults: Object.freeze([
|
|
316
|
-
"
|
|
317
|
-
"
|
|
318
|
-
"
|
|
320
|
+
"Shows semantic placement targets from app placement topology by default.",
|
|
321
|
+
"Use --concrete to inspect low-level ShellOutlet recipients.",
|
|
322
|
+
"Use --all to show both semantic placements and concrete recipients."
|
|
319
323
|
]),
|
|
320
|
-
fullUse: "jskit list-placements [--json]",
|
|
324
|
+
fullUse: "jskit list-placements [--concrete] [--all] [--json]",
|
|
321
325
|
showHelpOnBareInvocation: false,
|
|
322
326
|
handlerName: "commandListPlacements",
|
|
323
|
-
allowedFlagKeys: Object.freeze(["json"]),
|
|
327
|
+
allowedFlagKeys: Object.freeze(["concrete", "all", "json"]),
|
|
324
328
|
inlineOptionMode: "none",
|
|
325
329
|
allowedValueOptionNames: Object.freeze([])
|
|
326
330
|
}),
|
|
@@ -5,7 +5,10 @@ import {
|
|
|
5
5
|
writeFile
|
|
6
6
|
} from "node:fs/promises";
|
|
7
7
|
import path from "node:path";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
discoverPlacementTopologyFromApp,
|
|
10
|
+
discoverShellOutletTargetsFromApp
|
|
11
|
+
} from "@jskit-ai/kernel/server/support";
|
|
9
12
|
import { createCliError } from "../shared/cliError.js";
|
|
10
13
|
import {
|
|
11
14
|
createColorFormatter,
|
|
@@ -146,7 +149,8 @@ const commandHandlers = createCommandHandlers(
|
|
|
146
149
|
removeManagedViteProxyEntries,
|
|
147
150
|
hashBuffer,
|
|
148
151
|
rm,
|
|
149
|
-
discoverShellOutletTargetsFromApp
|
|
152
|
+
discoverShellOutletTargetsFromApp,
|
|
153
|
+
discoverPlacementTopologyFromApp
|
|
150
154
|
})
|
|
151
155
|
);
|
|
152
156
|
|