runcloud 0.2.190 → 0.2.192
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/dist/commands/run-cloud-fleet-release-control.js +24 -0
- package/dist/commands/run-cloud-fleet.js +2 -0
- package/dist/commands/run-cloud.js +73 -0
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/runcloud-0.2.192.tgz +0 -0
- package/skills/run-cloud-ios-simulator/SKILL.md +1 -1
- package/runcloud-0.2.190.tgz +0 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { readFileSync, statSync } from 'node:fs';
|
|
2
|
+
const path = '/run-cloud/admin/fleet-release-control';
|
|
3
|
+
export function releaseControlRequest(file) {
|
|
4
|
+
const stat = statSync(file);
|
|
5
|
+
if (!stat.isFile() || stat.size > 512 * 1024)
|
|
6
|
+
throw new Error('Use a JSON request file smaller than 512 KiB');
|
|
7
|
+
const value = JSON.parse(readFileSync(file, 'utf8'));
|
|
8
|
+
if (!value || typeof value !== 'object' || Array.isArray(value) || typeof value.reason !== 'string' || !value.reason.trim()
|
|
9
|
+
|| !Number.isSafeInteger(value.expectedRevision) || Number(value.expectedRevision) < 0
|
|
10
|
+
|| !['bootstrap', 'enroll', 'announce', 'override', 'pause', 'resume', 'rollback', 'defer'].includes(String(value.action))) {
|
|
11
|
+
throw new Error('Request needs action, reason and expectedRevision from the current release-control status');
|
|
12
|
+
}
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
export function registerFleetReleaseControlCommands(fleet, client, action) {
|
|
16
|
+
const release = fleet.command('release-control').description('Audited fleet targets and coordinated health-gated serve-sim rollouts');
|
|
17
|
+
release.command('status').description('Read targets, rollout stages, gate values and audit history').action(() => action(async () => {
|
|
18
|
+
console.log(JSON.stringify(await client().get(path, { signal: AbortSignal.timeout(30000) }), null, 2));
|
|
19
|
+
}));
|
|
20
|
+
release.command('apply <request.json>').description('Apply an audited target or rollout request; pause stops new grants while existing holders finish safely')
|
|
21
|
+
.action((file) => action(async () => {
|
|
22
|
+
console.log(JSON.stringify(await client().post(path, releaseControlRequest(file), { signal: AbortSignal.timeout(30000) }), null, 2));
|
|
23
|
+
}));
|
|
24
|
+
}
|
|
@@ -5,6 +5,7 @@ import { ApiClient, ApiError } from '../api.js';
|
|
|
5
5
|
import { requireCredentials } from '../config.js';
|
|
6
6
|
import { surveyFleet, surveyText } from './run-cloud-fleet-survey.js';
|
|
7
7
|
import { listReleaseChannels, promoteReleaseChannel, releaseText } from './run-cloud-fleet-releases.js';
|
|
8
|
+
import { registerFleetReleaseControlCommands } from './run-cloud-fleet-release-control.js';
|
|
8
9
|
const ROOT = '/run-cloud/admin/simulators';
|
|
9
10
|
const RANGES = ['1h', '6h', '24h', '7d', '30d', '90d', '1y', '2y'];
|
|
10
11
|
export const ACCESSIBILITY_CODES = ['accessibility_device_unavailable', 'accessibility_capture_failed'];
|
|
@@ -303,6 +304,7 @@ function printReport(report, json) {
|
|
|
303
304
|
}
|
|
304
305
|
export function registerRunCloudFleet(program) {
|
|
305
306
|
const fleet = program.command('fleet').description('Admin fleet telemetry, diagnosis, events, failure evidence and serve-sim release channels');
|
|
307
|
+
registerFleetReleaseControlCommands(fleet, credentialsClient, action);
|
|
306
308
|
fleet.command('survey [hostId]').description('Survey all fleet signals in seconds; authenticated admin reads, no local services')
|
|
307
309
|
.option('--json', 'Structured metrics, diagnosis, observations and coverage')
|
|
308
310
|
.option('--range <range>', 'Alert/input-lag window: 1h, 6h, 24h, 7d', '24h')
|
|
@@ -1033,6 +1033,52 @@ async function openSimulatorSessionUrl(platform, sessionId, url) {
|
|
|
1033
1033
|
throw error;
|
|
1034
1034
|
}
|
|
1035
1035
|
}
|
|
1036
|
+
function parseCoordinate(value, name) {
|
|
1037
|
+
const limit = name === 'latitude' ? 90 : 180;
|
|
1038
|
+
const parsed = Number(value);
|
|
1039
|
+
if (value.trim() === '' || !Number.isFinite(parsed) || Math.abs(parsed) > limit) {
|
|
1040
|
+
throw new Error(`--${name} must be a number between -${limit} and ${limit}`);
|
|
1041
|
+
}
|
|
1042
|
+
return parsed;
|
|
1043
|
+
}
|
|
1044
|
+
async function setSimulatorSessionLocation(platform, sessionId, point) {
|
|
1045
|
+
const path = `/run-cloud/${platform}/${encodeURIComponent(sessionId)}/location`;
|
|
1046
|
+
try {
|
|
1047
|
+
const response = (point ? await client().post(path, point) : await client().delete(path));
|
|
1048
|
+
const result = typeof response === 'object' && response !== null && !Array.isArray(response)
|
|
1049
|
+
? response
|
|
1050
|
+
: {};
|
|
1051
|
+
if (result.ok !== true || result.platform !== platform || result.sessionId !== sessionId || result.cleared !== (point === null)) {
|
|
1052
|
+
throw new ApiError(0, 'Simulator location returned an invalid acknowledgement', path, {
|
|
1053
|
+
code: 'location_invalid_response',
|
|
1054
|
+
retryable: true,
|
|
1055
|
+
sessionId,
|
|
1056
|
+
platform,
|
|
1057
|
+
action: 'location',
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
return result;
|
|
1061
|
+
}
|
|
1062
|
+
catch (error) {
|
|
1063
|
+
if (error instanceof ApiTransportError) {
|
|
1064
|
+
throw new ApiError(0, 'Simulator location could not reach the run.cloud API', path, {
|
|
1065
|
+
code: 'location_transport_error',
|
|
1066
|
+
retryable: true,
|
|
1067
|
+
sessionId,
|
|
1068
|
+
platform,
|
|
1069
|
+
action: 'location',
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1072
|
+
if (error instanceof ApiError) {
|
|
1073
|
+
Object.assign(error, {
|
|
1074
|
+
sessionId: error.sessionId ?? sessionId,
|
|
1075
|
+
platform: error.platform ?? platform,
|
|
1076
|
+
action: error.action ?? 'location',
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
throw error;
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1036
1082
|
function registerSimulatorCommands(program, platform) {
|
|
1037
1083
|
const label = platform === 'android' ? 'Android emulator' : 'iOS simulator';
|
|
1038
1084
|
const article = platform === 'android' ? 'an' : 'an';
|
|
@@ -1133,6 +1179,33 @@ function registerSimulatorCommands(program, platform) {
|
|
|
1133
1179
|
.action((url, opts) => action(async () => {
|
|
1134
1180
|
print(await openSimulatorSessionUrl(platform, opts.id, url), opts);
|
|
1135
1181
|
}, opts));
|
|
1182
|
+
const location = simulator
|
|
1183
|
+
.command('location')
|
|
1184
|
+
.description(`Set or clear the simulated GPS location of ${article} ${label} session`);
|
|
1185
|
+
location
|
|
1186
|
+
.command('set')
|
|
1187
|
+
.description('Set the simulated location (WGS84 degrees)')
|
|
1188
|
+
.argument('<id>', 'session id')
|
|
1189
|
+
.requiredOption('--latitude <degrees>', 'latitude from -90 to 90')
|
|
1190
|
+
.requiredOption('--longitude <degrees>', 'longitude from -180 to 180')
|
|
1191
|
+
.option('--json', 'output JSON', false)
|
|
1192
|
+
.action((id, opts) => action(async () => {
|
|
1193
|
+
const point = {
|
|
1194
|
+
latitude: parseCoordinate(opts.latitude, 'latitude'),
|
|
1195
|
+
longitude: parseCoordinate(opts.longitude, 'longitude'),
|
|
1196
|
+
};
|
|
1197
|
+
print(await setSimulatorSessionLocation(platform, id, point), opts);
|
|
1198
|
+
}, opts));
|
|
1199
|
+
location
|
|
1200
|
+
.command('clear')
|
|
1201
|
+
.description(platform === 'android'
|
|
1202
|
+
? 'Return to the emulator default location'
|
|
1203
|
+
: 'Remove the simulated location')
|
|
1204
|
+
.argument('<id>', 'session id')
|
|
1205
|
+
.option('--json', 'output JSON', false)
|
|
1206
|
+
.action((id, opts) => action(async () => {
|
|
1207
|
+
print(await setSimulatorSessionLocation(platform, id, null), opts);
|
|
1208
|
+
}, opts));
|
|
1136
1209
|
addInteractionOptions(simulator
|
|
1137
1210
|
.command('tap')
|
|
1138
1211
|
.description(`Tap normalized display coordinates in ${article} ${label} session`)
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const CLI_VERSION = '0.2.
|
|
1
|
+
export const CLI_VERSION = '0.2.192';
|
package/package.json
CHANGED
|
Binary file
|
|
@@ -40,7 +40,7 @@ runcloud ios get "$SESSION_ID" --json
|
|
|
40
40
|
```
|
|
41
41
|
|
|
42
42
|
Replace `ios` with `android` for an Android artifact. The shared lifecycle is
|
|
43
|
-
`create`, `list`, `get`, `open-url`, `logs`, and `delete`. Use `--json` whenever
|
|
43
|
+
`create`, `list`, `get`, `open-url`, `location set|clear`, `logs`, and `delete`. Use `--json` whenever
|
|
44
44
|
another program consumes output, and inspect `runcloud ios|android --help` for
|
|
45
45
|
create and log options.
|
|
46
46
|
|
package/runcloud-0.2.190.tgz
DELETED
|
Binary file
|