@ceralive/modem-control 1.1.0 → 1.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/README.md +35 -6
- package/dist/backend/at-lease.d.ts +3 -1
- package/dist/backend/at-lease.js +11 -2
- package/dist/backend/recovery-ladder.d.ts +1 -1
- package/dist/backend/transition-preconditions.d.ts +35 -10
- package/dist/backend/transition-preconditions.js +78 -10
- package/dist/backend/usb-mode-transition.js +39 -19
- package/dist/capability/five-g-preference.d.ts +2 -2
- package/dist/capability/support-claim.d.ts +2 -2
- package/dist/domain/shadow-divergence.d.ts +1 -1
- package/dist/fcc/coverage.d.ts +20 -20
- package/dist/hardware/router-parsers.d.ts +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/journal/legacy-ceraui.d.ts +2 -2
- package/dist/observations/metric.d.ts +15 -1
- package/dist/observations/provenance.d.ts +2 -2
- package/dist/observations/reading.d.ts +1 -1
- package/dist/observations/state-separation.d.ts +1 -1
- package/dist/operation-ids.d.ts +2 -0
- package/dist/operation-ids.js +26 -0
- package/dist/ports/location.d.ts +1 -1
- package/dist/providers/contracts.d.ts +2 -2
- package/dist/providers/huawei-hilink/provider.d.ts +12 -12
- package/dist/providers/huawei-hilink/runtime.d.ts +2 -2
- package/dist/providers/modem-manager/errors.d.ts +1 -1
- package/dist/providers/modem-manager/generic-operations.d.ts +3 -1
- package/dist/providers/modem-manager/generic-operations.js +3 -1
- package/dist/providers/modem-manager/index.d.ts +1 -0
- package/dist/providers/modem-manager/index.js +1 -0
- package/dist/providers/modem-manager/provider.d.ts +2 -0
- package/dist/providers/modem-manager/provider.js +3 -0
- package/dist/providers/modem-manager/runtime-composition-operation.d.ts +32 -0
- package/dist/providers/modem-manager/runtime-composition-operation.js +153 -0
- package/dist/providers/modem-manager/types.d.ts +2 -0
- package/dist/providers/network-manager/types.d.ts +11 -3
- package/dist/providers/ufi-himi/operations.d.ts +1 -1
- package/dist/providers/ufi-himi/prohibitions.d.ts +22 -22
- package/dist/providers/ufi-himi/provider.d.ts +4 -4
- package/dist/providers/ufi-himi/qualcomm-evidence.d.ts +1 -1
- package/dist/providers/ufi-himi/transport.d.ts +2 -2
- package/dist/providers/zte-goform/provider.d.ts +18 -13
- package/dist/providers/zte-goform/provider.js +16 -16
- package/dist/providers/zte-goform/session.d.ts +8 -2
- package/dist/providers/zte-goform/session.js +95 -31
- package/dist/radio/mode-combinations.d.ts +11 -1
- package/dist/safety/flock-resource-ownership.js +18 -31
- package/dist/usb-mode/catalog-schema.d.ts +18 -18
- package/dist/usb-mode/index.d.ts +1 -0
- package/dist/usb-mode/index.js +1 -0
- package/dist/usb-mode/ingestion.d.ts +4 -4
- package/dist/usb-mode/runtime-capability.d.ts +59 -0
- package/dist/usb-mode/runtime-capability.js +157 -0
- package/dist/ussd/refusal.d.ts +1 -1
- package/dist/ussd/session.d.ts +24 -2
- package/package.json +1 -1
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const MODEM_OPERATION_IDS: readonly ["modemmanager.radio-modes", "modemmanager.mode-combination", "modemmanager.bands", "modemmanager.signal", "modemmanager.sim", "modemmanager.power", "modemmanager.usb-composition", "status", "signal", "mode", "data", "ufi.signal.read", "ufi.details.read", "nv.write", "efs.write", "identity.write", "calibration.write", "firmware.flash", "edl.automation", "driver.blind-retry", "interface.blind-retry", "diag.write", "diag.info-probe", "shell.transport-fallback"];
|
|
2
|
+
export type ModemOperationId = (typeof MODEM_OPERATION_IDS)[number];
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export const MODEM_OPERATION_IDS = Object.freeze([
|
|
2
|
+
'modemmanager.radio-modes',
|
|
3
|
+
'modemmanager.mode-combination',
|
|
4
|
+
'modemmanager.bands',
|
|
5
|
+
'modemmanager.signal',
|
|
6
|
+
'modemmanager.sim',
|
|
7
|
+
'modemmanager.power',
|
|
8
|
+
'modemmanager.usb-composition',
|
|
9
|
+
'status',
|
|
10
|
+
'signal',
|
|
11
|
+
'mode',
|
|
12
|
+
'data',
|
|
13
|
+
'ufi.signal.read',
|
|
14
|
+
'ufi.details.read',
|
|
15
|
+
'nv.write',
|
|
16
|
+
'efs.write',
|
|
17
|
+
'identity.write',
|
|
18
|
+
'calibration.write',
|
|
19
|
+
'firmware.flash',
|
|
20
|
+
'edl.automation',
|
|
21
|
+
'driver.blind-retry',
|
|
22
|
+
'interface.blind-retry',
|
|
23
|
+
'diag.write',
|
|
24
|
+
'diag.info-probe',
|
|
25
|
+
'shell.transport-fallback',
|
|
26
|
+
]);
|
package/dist/ports/location.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type { ModemRef } from './modem-manager.js';
|
|
|
5
5
|
* names. `3gpp-lac-ci` is deliberately absent: coarse cell location is the cell-info
|
|
6
6
|
* module's surface, and MM advertises it on devices with no GNSS receiver at all.
|
|
7
7
|
*/
|
|
8
|
-
export declare const GNSS_SOURCES: readonly [
|
|
8
|
+
export declare const GNSS_SOURCES: readonly ['gps-raw', 'gps-nmea', 'gps-unmanaged', 'agps-msa', 'agps-msb'];
|
|
9
9
|
export type GnssSource = (typeof GNSS_SOURCES)[number];
|
|
10
10
|
/** What `Modem.Location` advertises and what is switched on right now. */
|
|
11
11
|
export interface LocationStatus {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { DeviceGeneration, ObservationEnvelope, OperationDescriptor, OperationResult, PhysicalModemId } from '../domain/index.js';
|
|
2
|
-
export declare const PROVIDER_TRANSPORTS: readonly [
|
|
2
|
+
export declare const PROVIDER_TRANSPORTS: readonly ['usb', 'pci', 'network', 'modemmanager'];
|
|
3
3
|
export type ProviderTransport = (typeof PROVIDER_TRANSPORTS)[number];
|
|
4
|
-
export declare const PASSIVE_FACT_KINDS: readonly [
|
|
4
|
+
export declare const PASSIVE_FACT_KINDS: readonly ['usb', 'pci', 'interface', 'driver', 'gateway', 'model', 'firmware'];
|
|
5
5
|
export type PassiveFactKind = (typeof PASSIVE_FACT_KINDS)[number];
|
|
6
6
|
export type EvidenceStrength = 'none' | 'weak' | 'moderate' | 'strong';
|
|
7
7
|
export type MatcherScore = 'unsupported' | 'maybe' | 'likely' | 'supported';
|
|
@@ -4,14 +4,14 @@ import type { ProviderDefinition, ProviderOperationsSurface } from '../contracts
|
|
|
4
4
|
import { HuaweiProvider, type ReadOperation, type WriteOperation } from './runtime.js';
|
|
5
5
|
import type { HilinkTransport } from './transport.js';
|
|
6
6
|
export declare const HILINK_PATHS: {
|
|
7
|
-
readonly session:
|
|
8
|
-
readonly loginState:
|
|
9
|
-
readonly login:
|
|
10
|
-
readonly status:
|
|
11
|
-
readonly signal:
|
|
12
|
-
readonly modeList:
|
|
13
|
-
readonly mode:
|
|
14
|
-
readonly data:
|
|
7
|
+
readonly session: '/api/webserver/SesTokInfo';
|
|
8
|
+
readonly loginState: '/api/user/state-login';
|
|
9
|
+
readonly login: '/api/user/login';
|
|
10
|
+
readonly status: '/api/monitoring/status';
|
|
11
|
+
readonly signal: '/api/device/signal';
|
|
12
|
+
readonly modeList: '/api/net/net-mode-list';
|
|
13
|
+
readonly mode: '/api/net/net-mode';
|
|
14
|
+
readonly data: '/api/dialup/mobile-dataswitch';
|
|
15
15
|
};
|
|
16
16
|
export type HilinkProfile = {
|
|
17
17
|
readonly id: string;
|
|
@@ -19,12 +19,12 @@ export type HilinkProfile = {
|
|
|
19
19
|
readonly passwordType: 3 | 4;
|
|
20
20
|
};
|
|
21
21
|
export declare const HILINK_PROFILES: readonly [{
|
|
22
|
-
readonly id:
|
|
23
|
-
readonly firmware:
|
|
22
|
+
readonly id: 'e3372h-22.200-password-type-3';
|
|
23
|
+
readonly firmware: '22.200.05.00.1080';
|
|
24
24
|
readonly passwordType: 3;
|
|
25
25
|
}, {
|
|
26
|
-
readonly id:
|
|
27
|
-
readonly firmware:
|
|
26
|
+
readonly id: 'e3372h-22.333-password-type-4';
|
|
27
|
+
readonly firmware: '22.333.01.00.00';
|
|
28
28
|
readonly passwordType: 4;
|
|
29
29
|
}];
|
|
30
30
|
export type HilinkCredentials = {
|
|
@@ -5,8 +5,8 @@ import { HilinkSessionRuntime } from './session.js';
|
|
|
5
5
|
export type { ReadOperation, WriteOperation } from './operations.js';
|
|
6
6
|
export declare class HuaweiProvider extends HilinkSessionRuntime {
|
|
7
7
|
sessionProbe(request: ProviderMatchRequest): Promise<{
|
|
8
|
-
signal: "
|
|
9
|
-
strength:
|
|
8
|
+
signal: "match" | "unknown";
|
|
9
|
+
strength: 'strong';
|
|
10
10
|
profiles: ("e3372h-22.200-password-type-3" | "e3372h-22.333-password-type-4")[];
|
|
11
11
|
detail: string;
|
|
12
12
|
}>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const MODEM_MANAGER_REFUSAL_REASONS: readonly [
|
|
1
|
+
export declare const MODEM_MANAGER_REFUSAL_REASONS: readonly ['unauthorized', 'unsupported', 'wrong-state', 'busy', 'not-found', 'timed-out', 'disconnected', 'failed'];
|
|
2
2
|
export type ModemManagerRefusalReason = (typeof MODEM_MANAGER_REFUSAL_REASONS)[number];
|
|
3
3
|
export type ModemManagerRefusal = {
|
|
4
4
|
readonly reason: ModemManagerRefusalReason;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { MmDbusBackend } from '../../backend/index.js';
|
|
2
2
|
import type { ProviderExecutionContext } from '../contracts.js';
|
|
3
|
+
import { type RuntimeCompositionOperationDeps } from './runtime-composition-operation.js';
|
|
3
4
|
import type { ModemManagerProviderOperations, ModemManagerSnapshotResult } from './types.js';
|
|
4
5
|
export interface GenericOperationsDeps {
|
|
5
6
|
readonly backend: MmDbusBackend;
|
|
7
|
+
readonly runtimeComposition?: RuntimeCompositionOperationDeps;
|
|
6
8
|
readSnapshot(context: ProviderExecutionContext): Promise<ModemManagerSnapshotResult>;
|
|
7
9
|
}
|
|
8
|
-
export declare function createGenericOperations(deps: GenericOperationsDeps): Pick<ModemManagerProviderOperations, 'radio' | 'modes' | 'bands' | 'signal' | 'sim' | 'power'>;
|
|
10
|
+
export declare function createGenericOperations(deps: GenericOperationsDeps): Pick<ModemManagerProviderOperations, 'radio' | 'modes' | 'bands' | 'signal' | 'sim' | 'power' | 'usbComposition'>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { classifyOperationCompletion, defineOperationDescriptor, runtimePath, } from '../../domain/index.js';
|
|
2
2
|
import { bandWriteReadbackMatches, buildBandWriteDescriptor, buildModeWriteDescriptor, encodeModeSelection, matchAdvertisedCombination, sameSelection, selectionOf, } from '../../radio/index.js';
|
|
3
3
|
import { mapModemManagerError } from './errors.js';
|
|
4
|
+
import { createRuntimeCompositionOperation, } from './runtime-composition-operation.js';
|
|
4
5
|
const PROVIDER_ID = 'modemmanager';
|
|
5
6
|
const PROFILE = 'generic-mm';
|
|
6
7
|
function readDescriptor(id) {
|
|
@@ -203,5 +204,6 @@ export function createGenericOperations(deps) {
|
|
|
203
204
|
descriptor: readDescriptor('modemmanager.power'),
|
|
204
205
|
read: (context) => readField(context, 'powerRead', (snapshot) => snapshot.power, 'power-read-unsupported'),
|
|
205
206
|
};
|
|
206
|
-
|
|
207
|
+
const usbComposition = createRuntimeCompositionOperation(deps.runtimeComposition);
|
|
208
|
+
return { radio, modes, bands, signal, sim, power, usbComposition };
|
|
207
209
|
}
|
|
@@ -4,6 +4,7 @@ import { type NormalizedModemObservation } from '../../observations/index.js';
|
|
|
4
4
|
import type { ObservationListener } from '../../ports/index.js';
|
|
5
5
|
import type { DbusTransport } from '../../transport/index.js';
|
|
6
6
|
import type { ProviderDefinition, ProviderExecutionContext } from '../contracts.js';
|
|
7
|
+
import type { RuntimeCompositionOperationDeps } from './runtime-composition-operation.js';
|
|
7
8
|
import type { ModemManagerProviderLifecycle, ModemManagerProviderOperations, ModemManagerSnapshotResult } from './types.js';
|
|
8
9
|
export interface ModemManagerProviderOptions {
|
|
9
10
|
readonly transport: DbusTransport;
|
|
@@ -26,6 +27,7 @@ export interface ModemManagerProviderOptions {
|
|
|
26
27
|
* hence no band write: fail-closed, which is this module's whole stance.
|
|
27
28
|
*/
|
|
28
29
|
readonly bandSku?: (context: ProviderExecutionContext) => BandSku | undefined;
|
|
30
|
+
readonly runtimeComposition?: RuntimeCompositionOperationDeps;
|
|
29
31
|
}
|
|
30
32
|
export declare class ModemManagerProvider implements ModemManagerProviderLifecycle {
|
|
31
33
|
#private;
|
|
@@ -60,6 +60,9 @@ export class ModemManagerProvider {
|
|
|
60
60
|
...createGenericOperations({
|
|
61
61
|
backend: this.#backend,
|
|
62
62
|
readSnapshot: (context) => this.readSnapshot(context),
|
|
63
|
+
...(options.runtimeComposition === undefined
|
|
64
|
+
? {}
|
|
65
|
+
: { runtimeComposition: options.runtimeComposition }),
|
|
63
66
|
}),
|
|
64
67
|
...this.#moduleOperations.operations,
|
|
65
68
|
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type AtCommandSender, type UsbModeTransitionOutcome } from '../../backend/index.js';
|
|
2
|
+
import { type RuntimeCompositionCapability, type RuntimeCompositionMode } from '../../usb-mode/index.js';
|
|
3
|
+
import type { ProviderExecutionContext } from '../contracts.js';
|
|
4
|
+
import type { ContextWriteOperation } from './types.js';
|
|
5
|
+
export declare const RUNTIME_COMPOSITION_SUPPRESSIONS: readonly ['unknown-vendor', 'no-return-path', 'blocked-by-state', 'provisioning-disabled'];
|
|
6
|
+
export type RuntimeCompositionSuppression = (typeof RUNTIME_COMPOSITION_SUPPRESSIONS)[number];
|
|
7
|
+
export type RuntimeCompositionState = {
|
|
8
|
+
readonly status: 'available';
|
|
9
|
+
readonly current: RuntimeCompositionMode;
|
|
10
|
+
readonly enumerated: readonly RuntimeCompositionMode[];
|
|
11
|
+
readonly offerable: readonly RuntimeCompositionMode[];
|
|
12
|
+
} | {
|
|
13
|
+
readonly status: 'suppressed';
|
|
14
|
+
readonly reason: RuntimeCompositionSuppression;
|
|
15
|
+
readonly detail: string;
|
|
16
|
+
readonly current: RuntimeCompositionMode | null;
|
|
17
|
+
readonly enumerated: readonly RuntimeCompositionMode[];
|
|
18
|
+
readonly offerable: readonly [];
|
|
19
|
+
};
|
|
20
|
+
export type RuntimeCompositionOperationDeps = {
|
|
21
|
+
readonly vendor: (context: ProviderExecutionContext) => string | Promise<string>;
|
|
22
|
+
readonly provisioningEnabled: (context: ProviderExecutionContext) => boolean | Promise<boolean>;
|
|
23
|
+
readonly blockedReason: (context: ProviderExecutionContext) => string | undefined | Promise<string | undefined>;
|
|
24
|
+
readonly atSender: AtCommandSender;
|
|
25
|
+
readonly transition: (context: ProviderExecutionContext, capability: Extract<RuntimeCompositionCapability, {
|
|
26
|
+
readonly status: 'available';
|
|
27
|
+
}>, target: RuntimeCompositionMode) => Promise<UsbModeTransitionOutcome>;
|
|
28
|
+
};
|
|
29
|
+
export interface RuntimeCompositionOperation extends ContextWriteOperation<RuntimeCompositionMode, RuntimeCompositionState> {
|
|
30
|
+
capability(context: ProviderExecutionContext): Promise<RuntimeCompositionState>;
|
|
31
|
+
}
|
|
32
|
+
export declare function createRuntimeCompositionOperation(deps: RuntimeCompositionOperationDeps | undefined): RuntimeCompositionOperation;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { AtCommandLease, computeAtAllowlist, } from '../../backend/index.js';
|
|
2
|
+
import { classifyOperationCompletion, defineOperationDescriptor, } from '../../domain/index.js';
|
|
3
|
+
import { isRuntimeCompositionVendor, RUNTIME_COMPOSITION_QUERY_REGISTRY, resolveRuntimeCompositionCapability, } from '../../usb-mode/index.js';
|
|
4
|
+
export const RUNTIME_COMPOSITION_SUPPRESSIONS = [
|
|
5
|
+
'unknown-vendor',
|
|
6
|
+
'no-return-path',
|
|
7
|
+
'blocked-by-state',
|
|
8
|
+
'provisioning-disabled',
|
|
9
|
+
];
|
|
10
|
+
const OPERATION_ID = 'modemmanager.usb-composition';
|
|
11
|
+
function suppressed(reason, detail, capability) {
|
|
12
|
+
return {
|
|
13
|
+
status: 'suppressed',
|
|
14
|
+
reason,
|
|
15
|
+
detail,
|
|
16
|
+
current: capability?.current ?? null,
|
|
17
|
+
enumerated: capability?.enumerated ?? [],
|
|
18
|
+
offerable: [],
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function descriptorFor(state) {
|
|
22
|
+
const available = state.status === 'available';
|
|
23
|
+
return defineOperationDescriptor({
|
|
24
|
+
id: OPERATION_ID,
|
|
25
|
+
support: {
|
|
26
|
+
read: { supported: true },
|
|
27
|
+
write: available ? { supported: true } : { supported: false, reason: state.reason },
|
|
28
|
+
},
|
|
29
|
+
authority: 'provider',
|
|
30
|
+
provider: 'modemmanager',
|
|
31
|
+
constraints: {
|
|
32
|
+
kind: 'allowed-values',
|
|
33
|
+
values: available ? state.offerable : [],
|
|
34
|
+
},
|
|
35
|
+
livePreconditions: [
|
|
36
|
+
'modem-present',
|
|
37
|
+
'runtime-interface-present',
|
|
38
|
+
'identity-confident',
|
|
39
|
+
'streaming-interlock-open',
|
|
40
|
+
],
|
|
41
|
+
availability: available ? { state: 'available' } : { state: 'refused', reason: state.reason },
|
|
42
|
+
mutationImpact: 'disruptive',
|
|
43
|
+
retryClass: 'never',
|
|
44
|
+
readback: {
|
|
45
|
+
required: true,
|
|
46
|
+
reason: 'device-reported-composition',
|
|
47
|
+
matches: (input, value) => value.status === 'available' && Object.is(input, value.current),
|
|
48
|
+
},
|
|
49
|
+
rollback: { required: true, reason: 'restore-previous-composition' },
|
|
50
|
+
journal: { required: true, reason: 'composition-reenumeration' },
|
|
51
|
+
admission: { required: true, reason: 'provider-mutation' },
|
|
52
|
+
evidence: { profiles: ['generic-mm'], firmware: [] },
|
|
53
|
+
confidence: available ? 'high' : 'unknown',
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
function result(context, completion) {
|
|
57
|
+
return classifyOperationCompletion({
|
|
58
|
+
operation: 'write',
|
|
59
|
+
completionGeneration: context.generation,
|
|
60
|
+
currentGeneration: context.generation,
|
|
61
|
+
completion,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
function disabledOperation() {
|
|
65
|
+
const state = {
|
|
66
|
+
status: 'suppressed',
|
|
67
|
+
reason: 'provisioning-disabled',
|
|
68
|
+
detail: 'USB composition provisioning is not configured',
|
|
69
|
+
current: null,
|
|
70
|
+
enumerated: [],
|
|
71
|
+
offerable: [],
|
|
72
|
+
};
|
|
73
|
+
const descriptor = descriptorFor(state);
|
|
74
|
+
return {
|
|
75
|
+
descriptor,
|
|
76
|
+
describe: () => Promise.resolve(descriptor),
|
|
77
|
+
capability: () => Promise.resolve(state),
|
|
78
|
+
read: (context) => Promise.resolve(result(context, { status: 'applied', value: state })),
|
|
79
|
+
write: (context) => Promise.resolve(result(context, { status: 'refused', reason: state.reason })),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
export function createRuntimeCompositionOperation(deps) {
|
|
83
|
+
if (deps === undefined)
|
|
84
|
+
return disabledOperation();
|
|
85
|
+
const capability = async (context) => {
|
|
86
|
+
const vendor = (await deps.vendor(context)).trim().toLowerCase();
|
|
87
|
+
if (!isRuntimeCompositionVendor(vendor))
|
|
88
|
+
return suppressed('unknown-vendor', `No reviewed composition query exists for ${vendor}`);
|
|
89
|
+
if (!(await deps.provisioningEnabled(context)))
|
|
90
|
+
return suppressed('provisioning-disabled', 'USB composition provisioning is disabled');
|
|
91
|
+
const blocked = await deps.blockedReason(context);
|
|
92
|
+
if (blocked !== undefined)
|
|
93
|
+
return suppressed('blocked-by-state', blocked);
|
|
94
|
+
const queries = RUNTIME_COMPOSITION_QUERY_REGISTRY[vendor];
|
|
95
|
+
const lease = new AtCommandLease({
|
|
96
|
+
sender: deps.atSender,
|
|
97
|
+
allowlist: computeAtAllowlist([]),
|
|
98
|
+
});
|
|
99
|
+
const current = await lease.run(queries.current);
|
|
100
|
+
const enumeration = await lease.run(queries.enumerate);
|
|
101
|
+
if (!current.ok || !enumeration.ok)
|
|
102
|
+
return suppressed('no-return-path', 'A runtime composition query failed');
|
|
103
|
+
const resolved = resolveRuntimeCompositionCapability({
|
|
104
|
+
vendor,
|
|
105
|
+
currentResponse: current.raw,
|
|
106
|
+
enumerationResponse: enumeration.raw,
|
|
107
|
+
});
|
|
108
|
+
if (resolved.status === 'unknown')
|
|
109
|
+
return suppressed('no-return-path', resolved.reason, resolved);
|
|
110
|
+
if (!resolved.returnPathProven)
|
|
111
|
+
return suppressed('no-return-path', 'The current mode is absent from the device catalog', resolved);
|
|
112
|
+
return {
|
|
113
|
+
status: 'available',
|
|
114
|
+
current: resolved.current,
|
|
115
|
+
enumerated: resolved.enumerated,
|
|
116
|
+
offerable: resolved.offerable.filter((mode) => !Object.is(mode, resolved.current)),
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
return {
|
|
120
|
+
descriptor: descriptorFor(suppressed('provisioning-disabled', 'Capability has not been read for this device')),
|
|
121
|
+
describe: async (context) => descriptorFor(await capability(context)),
|
|
122
|
+
capability,
|
|
123
|
+
read: async (context) => result(context, { status: 'applied', value: await capability(context) }),
|
|
124
|
+
write: async (context, target) => {
|
|
125
|
+
const state = await capability(context);
|
|
126
|
+
if (state.status === 'suppressed')
|
|
127
|
+
return result(context, { status: 'refused', reason: state.reason });
|
|
128
|
+
if (!state.offerable.some((candidate) => Object.is(candidate, target)))
|
|
129
|
+
return result(context, { status: 'refused', reason: 'no-return-path' });
|
|
130
|
+
const fullCapability = {
|
|
131
|
+
status: 'available',
|
|
132
|
+
current: state.current,
|
|
133
|
+
enumerated: state.enumerated,
|
|
134
|
+
returnPathProven: true,
|
|
135
|
+
offerable: [state.current, ...state.offerable],
|
|
136
|
+
};
|
|
137
|
+
const outcome = await deps.transition(context, fullCapability, target);
|
|
138
|
+
if (outcome.status === 'refused')
|
|
139
|
+
return result(context, { status: 'refused', reason: 'blocked-by-state' });
|
|
140
|
+
if (outcome.status === 'failed')
|
|
141
|
+
return result(context, { status: 'failed', reason: outcome.reason });
|
|
142
|
+
return result(context, {
|
|
143
|
+
status: 'applied',
|
|
144
|
+
value: {
|
|
145
|
+
status: 'available',
|
|
146
|
+
current: target,
|
|
147
|
+
enumerated: state.enumerated,
|
|
148
|
+
offerable: state.enumerated.filter((mode) => !Object.is(mode, target)),
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|
|
@@ -7,6 +7,7 @@ import type { BandWriteCertification, ModeSelection, RadioModeTruth } from '../.
|
|
|
7
7
|
import type { MmUssd, UssdVerbResult } from '../../ussd/index.js';
|
|
8
8
|
import type { ProviderExecutionContext, ProviderOperationsSurface } from '../contracts.js';
|
|
9
9
|
import type { ModemManagerRefusalReason } from './errors.js';
|
|
10
|
+
import type { RuntimeCompositionOperation } from './runtime-composition-operation.js';
|
|
10
11
|
export type ModemManagerCapabilities = {
|
|
11
12
|
readonly modeRead: boolean;
|
|
12
13
|
readonly modeWrite: boolean;
|
|
@@ -113,6 +114,7 @@ export interface ModemManagerProviderOperations extends ProviderOperationsSurfac
|
|
|
113
114
|
readonly signal: ContextReadOperation<ModemManagerSignalState>;
|
|
114
115
|
readonly sim: ContextReadOperation<ModemManagerSimState>;
|
|
115
116
|
readonly power: ContextReadOperation<RadioPower>;
|
|
117
|
+
readonly usbComposition: RuntimeCompositionOperation;
|
|
116
118
|
readonly location: {
|
|
117
119
|
status(context: ProviderExecutionContext): Promise<LocationStatusResult>;
|
|
118
120
|
enable(context: ProviderExecutionContext, sources: readonly GnssSource[]): Promise<LocationToggleResult>;
|
|
@@ -8,7 +8,7 @@ import type { ConnectionId, DeviceIfname, GsmProfileInput, Receipt } from '../..
|
|
|
8
8
|
* because a device in `prepare` carrying our connection is COMING UP, and reporting
|
|
9
9
|
* that as a lost bearer would turn every ordinary activation into a false alarm.
|
|
10
10
|
*/
|
|
11
|
-
export declare const NM_DEVICE_STATES: readonly [
|
|
11
|
+
export declare const NM_DEVICE_STATES: readonly ['unknown', 'unmanaged', 'unavailable', 'disconnected', 'prepare', 'config', 'need-auth', 'ip-config', 'ip-check', 'secondaries', 'activated', 'deactivating', 'failed'];
|
|
12
12
|
export type NmDeviceState = (typeof NM_DEVICE_STATES)[number];
|
|
13
13
|
/**
|
|
14
14
|
* The bearer identity all three slots compare in.
|
|
@@ -85,7 +85,7 @@ export interface NmDesiredRequest {
|
|
|
85
85
|
/** Update this existing profile instead of creating a new one. */
|
|
86
86
|
readonly connectionId?: ConnectionId;
|
|
87
87
|
}
|
|
88
|
-
export declare const NM_ADAPTER_REFUSAL_REASONS: readonly [
|
|
88
|
+
export declare const NM_ADAPTER_REFUSAL_REASONS: readonly ['no-desired-profile', 'profile-absent', 'activation-failed', 'deactivation-failed', 'write-failed'];
|
|
89
89
|
export type NmAdapterRefusalReason = (typeof NM_ADAPTER_REFUSAL_REASONS)[number];
|
|
90
90
|
export type NmSaveResult = {
|
|
91
91
|
readonly ok: true;
|
|
@@ -106,7 +106,15 @@ export type NmApplyResult = {
|
|
|
106
106
|
readonly reason: NmAdapterRefusalReason;
|
|
107
107
|
readonly receipt: Receipt;
|
|
108
108
|
};
|
|
109
|
-
export declare const NM_APPLIED_LOSS_REASONS: readonly [
|
|
109
|
+
export declare const NM_APPLIED_LOSS_REASONS: readonly [
|
|
110
|
+
/** The readout has no such device at all — a re-enumeration, or an unplug. */
|
|
111
|
+
'interface-absent',
|
|
112
|
+
/** The device is present and carries no active connection. */
|
|
113
|
+
'interface-detached',
|
|
114
|
+
/** A DIFFERENT connection is active on our device. */
|
|
115
|
+
'connection-replaced',
|
|
116
|
+
/** NM reports the device itself failed. */
|
|
117
|
+
'activation-failed'];
|
|
110
118
|
export type NmAppliedLossReason = (typeof NM_APPLIED_LOSS_REASONS)[number];
|
|
111
119
|
/**
|
|
112
120
|
* The applied bearer stopped being in force.
|
|
@@ -10,7 +10,7 @@ export type UfiReadValue = Readonly<Record<string, unknown>>;
|
|
|
10
10
|
* `write` member to omit — the absence is structural, not a convention this file keeps.
|
|
11
11
|
*/
|
|
12
12
|
export type UfiReadOperation = ProviderReadOperations<UfiReadValue>;
|
|
13
|
-
export declare const UFI_READ_OPERATION_IDS: readonly [
|
|
13
|
+
export declare const UFI_READ_OPERATION_IDS: readonly ['ufi.signal.read', 'ufi.details.read'];
|
|
14
14
|
export type UfiReadOperationId = (typeof UFI_READ_OPERATION_IDS)[number];
|
|
15
15
|
export declare function isUfiReadOperation(operationId: string): operationId is UfiReadOperationId;
|
|
16
16
|
export declare function ufiReadDescriptor(operationId: UfiReadOperationId): OperationDescriptor<never, UfiReadValue>;
|
|
@@ -2,48 +2,48 @@ import { type OperationDescriptor } from '../../domain/index.js';
|
|
|
2
2
|
export type UfiProhibitionClass = 'device-integrity' | 'firmware' | 'recovery-automation' | 'diagnostic-channel' | 'transport-fallback';
|
|
3
3
|
export declare const UFI_PROHIBITED_OPERATIONS: {
|
|
4
4
|
readonly 'nv.write': {
|
|
5
|
-
readonly reason:
|
|
6
|
-
readonly class:
|
|
5
|
+
readonly reason: 'nv-write-prohibited';
|
|
6
|
+
readonly class: 'device-integrity';
|
|
7
7
|
};
|
|
8
8
|
readonly 'efs.write': {
|
|
9
|
-
readonly reason:
|
|
10
|
-
readonly class:
|
|
9
|
+
readonly reason: 'efs-write-prohibited';
|
|
10
|
+
readonly class: 'device-integrity';
|
|
11
11
|
};
|
|
12
12
|
readonly 'identity.write': {
|
|
13
|
-
readonly reason:
|
|
14
|
-
readonly class:
|
|
13
|
+
readonly reason: 'identity-write-prohibited';
|
|
14
|
+
readonly class: 'device-integrity';
|
|
15
15
|
};
|
|
16
16
|
readonly 'calibration.write': {
|
|
17
|
-
readonly reason:
|
|
18
|
-
readonly class:
|
|
17
|
+
readonly reason: 'calibration-write-prohibited';
|
|
18
|
+
readonly class: 'device-integrity';
|
|
19
19
|
};
|
|
20
20
|
readonly 'firmware.flash': {
|
|
21
|
-
readonly reason:
|
|
22
|
-
readonly class:
|
|
21
|
+
readonly reason: 'firmware-flash-prohibited';
|
|
22
|
+
readonly class: 'firmware';
|
|
23
23
|
};
|
|
24
24
|
readonly 'edl.automation': {
|
|
25
|
-
readonly reason:
|
|
26
|
-
readonly class:
|
|
25
|
+
readonly reason: 'edl-automation-prohibited';
|
|
26
|
+
readonly class: 'recovery-automation';
|
|
27
27
|
};
|
|
28
28
|
readonly 'driver.blind-retry': {
|
|
29
|
-
readonly reason:
|
|
30
|
-
readonly class:
|
|
29
|
+
readonly reason: 'blind-driver-retry-prohibited';
|
|
30
|
+
readonly class: 'recovery-automation';
|
|
31
31
|
};
|
|
32
32
|
readonly 'interface.blind-retry': {
|
|
33
|
-
readonly reason:
|
|
34
|
-
readonly class:
|
|
33
|
+
readonly reason: 'blind-interface-retry-prohibited';
|
|
34
|
+
readonly class: 'recovery-automation';
|
|
35
35
|
};
|
|
36
36
|
readonly 'diag.write': {
|
|
37
|
-
readonly reason:
|
|
38
|
-
readonly class:
|
|
37
|
+
readonly reason: 'diag-write-prohibited';
|
|
38
|
+
readonly class: 'diagnostic-channel';
|
|
39
39
|
};
|
|
40
40
|
readonly 'diag.info-probe': {
|
|
41
|
-
readonly reason:
|
|
42
|
-
readonly class:
|
|
41
|
+
readonly reason: 'diag-probe-is-bench-supervised-only';
|
|
42
|
+
readonly class: 'diagnostic-channel';
|
|
43
43
|
};
|
|
44
44
|
readonly 'shell.transport-fallback': {
|
|
45
|
-
readonly reason:
|
|
46
|
-
readonly class:
|
|
45
|
+
readonly reason: 'shell-transport-fallback-prohibited';
|
|
46
|
+
readonly class: 'transport-fallback';
|
|
47
47
|
};
|
|
48
48
|
};
|
|
49
49
|
export type UfiProhibitedOperationId = keyof typeof UFI_PROHIBITED_OPERATIONS;
|
|
@@ -15,13 +15,13 @@ export type UfiOperationsSurface = ProviderOperationsSurface & {
|
|
|
15
15
|
};
|
|
16
16
|
export declare class UfiHimiProvider extends UfiSessionRuntime {
|
|
17
17
|
fingerprint(request: ProviderMatchRequest): Promise<{
|
|
18
|
-
signal:
|
|
19
|
-
strength:
|
|
18
|
+
signal: 'unknown';
|
|
19
|
+
strength: 'strong';
|
|
20
20
|
profiles: readonly string[];
|
|
21
21
|
detail: string;
|
|
22
22
|
} | {
|
|
23
|
-
signal: "
|
|
24
|
-
strength:
|
|
23
|
+
signal: "match" | "unknown";
|
|
24
|
+
strength: 'strong';
|
|
25
25
|
profiles: string[];
|
|
26
26
|
detail: string;
|
|
27
27
|
}>;
|
|
@@ -29,4 +29,4 @@ export declare function classifyUfiDiagEvidence(input: {
|
|
|
29
29
|
* Unconditional. A descriptor-confirmed DIAG channel raises what a SUPERVISED BENCH
|
|
30
30
|
* operator may attempt by hand; it never raises what this package may do on its own.
|
|
31
31
|
*/
|
|
32
|
-
export declare const UFI_DIAG_PRODUCTION_ACCESS:
|
|
32
|
+
export declare const UFI_DIAG_PRODUCTION_ACCESS: 'prohibited';
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/** The seven read commands the HIMI firmware answers. Bench-observed, not inferred. */
|
|
2
|
-
export declare const UFI_READ_COMMANDS: readonly [
|
|
2
|
+
export declare const UFI_READ_COMMANDS: readonly ['getoverview', 'getsysinfo', 'getallstatus', 'getapninfo', 'getnetworkmode', 'gethimiusbtether', 'getproduceinfo'];
|
|
3
3
|
export type UfiReadCommand = (typeof UFI_READ_COMMANDS)[number];
|
|
4
4
|
/**
|
|
5
5
|
* The ONE non-read command. It opens a server-side session and changes nothing on the
|
|
6
6
|
* device; the modem's configuration, radio and storage are untouched by it.
|
|
7
7
|
*/
|
|
8
|
-
export declare const UFI_SESSION_COMMANDS: readonly [
|
|
8
|
+
export declare const UFI_SESSION_COMMANDS: readonly ['login'];
|
|
9
9
|
export type UfiSessionCommand = (typeof UFI_SESSION_COMMANDS)[number];
|
|
10
10
|
export declare const UFI_COMMANDS: readonly ["getoverview", "getsysinfo", "getallstatus", "getapninfo", "getnetworkmode", "gethimiusbtether", "getproduceinfo", "login"];
|
|
11
11
|
export type UfiCommand = UfiReadCommand | UfiSessionCommand;
|
|
@@ -4,22 +4,27 @@ import type { CapabilityReader, ProviderDefinition, ProviderExecutionContext, Pr
|
|
|
4
4
|
import { ZteSessionRuntime } from './session.js';
|
|
5
5
|
import type { ZteTransport } from './transport.js';
|
|
6
6
|
export declare const ZTE_PATHS: {
|
|
7
|
-
readonly get:
|
|
8
|
-
readonly set:
|
|
7
|
+
readonly get: '/goform/goform_get_cmd_process';
|
|
8
|
+
readonly set: '/goform/goform_set_cmd_process';
|
|
9
9
|
};
|
|
10
|
+
export declare const ZTE_EVIDENCE_CMD = "LD,psw_fail_num_str,login_lock_time,wa_inner_version,cr_version";
|
|
10
11
|
export type ZteProfile = {
|
|
11
|
-
readonly id: 'mf79u-legacy' | 'mf266-salted';
|
|
12
|
+
readonly id: 'mf79u-legacy' | 'mf79u-ld-salted' | 'mf266-salted';
|
|
12
13
|
readonly firmware: 'MF79U' | 'MF266';
|
|
13
|
-
readonly algorithm: 'legacy-base64' | 'salted-sha256';
|
|
14
|
+
readonly algorithm: 'legacy-base64' | 'login-salted-sha256' | 'multi-user-salted-sha256';
|
|
14
15
|
};
|
|
15
16
|
export declare const ZTE_PROFILES: readonly [{
|
|
16
|
-
readonly id:
|
|
17
|
-
readonly firmware:
|
|
18
|
-
readonly algorithm:
|
|
17
|
+
readonly id: 'mf79u-legacy';
|
|
18
|
+
readonly firmware: 'MF79U';
|
|
19
|
+
readonly algorithm: 'legacy-base64';
|
|
19
20
|
}, {
|
|
20
|
-
readonly id:
|
|
21
|
-
readonly firmware:
|
|
22
|
-
readonly algorithm:
|
|
21
|
+
readonly id: 'mf79u-ld-salted';
|
|
22
|
+
readonly firmware: 'MF79U';
|
|
23
|
+
readonly algorithm: 'login-salted-sha256';
|
|
24
|
+
}, {
|
|
25
|
+
readonly id: 'mf266-salted';
|
|
26
|
+
readonly firmware: 'MF266';
|
|
27
|
+
readonly algorithm: 'multi-user-salted-sha256';
|
|
23
28
|
}];
|
|
24
29
|
export declare const ZTE_UNKNOWN_PROFILE = "zte-unknown-read-only";
|
|
25
30
|
export type ZteCredentials = {
|
|
@@ -34,12 +39,12 @@ export type ZteOptions = {
|
|
|
34
39
|
readonly now?: () => number;
|
|
35
40
|
};
|
|
36
41
|
type ZteOperations = ProviderOperationsSurface;
|
|
37
|
-
export declare function
|
|
42
|
+
export declare function zteProfilesForFirmware(firmware: string | undefined): readonly ZteProfile[];
|
|
38
43
|
export declare function zteProfileById(profileId: string): ZteProfile | undefined;
|
|
39
44
|
export declare class ZteGoformProvider extends ZteSessionRuntime {
|
|
40
45
|
fingerprint(request: ProviderMatchRequest): Promise<{
|
|
41
|
-
signal: "
|
|
42
|
-
strength:
|
|
46
|
+
signal: "match" | "unknown";
|
|
47
|
+
strength: 'strong';
|
|
43
48
|
profiles: string[];
|
|
44
49
|
detail: string;
|
|
45
50
|
}>;
|