@openmdm/core 0.4.0 → 0.6.0
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/index.d.ts +115 -2
- package/dist/index.js +33 -2
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +5 -2
- package/dist/types.js +6 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
- package/src/agent-protocol.ts +130 -0
- package/src/index.ts +19 -1
- package/src/types.ts +7 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,120 @@
|
|
|
1
1
|
import { WebhookConfig, MDMEvent, WebhookEndpoint, EventType, DatabaseAdapter, TenantManager, AuthorizationManager, AuditConfig, AuditManager, ScheduleManager, MessageQueueManager, DashboardManager, PluginStorageAdapter, MDMConfig, MDMInstance } from './types.js';
|
|
2
|
-
export { AppInstallationSummary, AppRollback, AppVersion, Application, ApplicationManager, ApplicationNotFoundError, AuditAction, AuditLog, AuditLogFilter, AuditLogListResult, AuditSummary, AuthConfig, AuthenticationError, AuthorizationError, Command, CommandFilter, CommandManager, CommandResult, CommandStatus, CommandSuccessRates, CommandType, CreateAppRollbackInput, CreateApplicationInput, CreateAuditLogInput, CreateDeviceInput, CreateGroupInput, CreatePolicyInput, CreateRoleInput, CreateScheduledTaskInput, CreateTenantInput, CreateUserInput, DashboardStats, DeployTarget, Device, DeviceFilter, DeviceListResult, DeviceLocation, DeviceManager, DeviceNotFoundError, DeviceStatus, DeviceStatusBreakdown, EnqueueMessageInput, EnrollmentConfig, EnrollmentError, EnrollmentMethod, EnrollmentRequest, EnrollmentResponse, EnrollmentTrendPoint, EventFilter, EventHandler, EventPayloadMap, Group, GroupHierarchyStats, GroupManager, GroupNotFoundError, GroupTreeNode, HardwareControl, Heartbeat, InstalledApp, MDMError, MDMPlugin, MaintenanceWindow, PasswordPolicy, Permission, PermissionAction, PermissionResource, PluginMiddleware, PluginRoute, PluginStorageEntry, Policy, PolicyApplication, PolicyManager, PolicyNotFoundError, PolicySettings, PushAdapter, PushBatchResult, PushConfig, PushMessage, PushProviderConfig, PushResult, PushToken, QueueMessageStatus, QueueStats, QueuedMessage, RegisterPushTokenInput, Role, RoleNotFoundError, ScheduledTask, ScheduledTaskFilter, ScheduledTaskListResult, ScheduledTaskStatus, SendCommandInput, StorageConfig, SystemUpdatePolicy, TaskExecution, TaskSchedule, TaskType, Tenant, TenantFilter, TenantListResult, TenantNotFoundError, TenantSettings, TenantStats, TenantStatus, TimeWindow, UpdateApplicationInput, UpdateDeviceInput, UpdateGroupInput, UpdatePolicyInput, UpdateRoleInput, UpdateScheduledTaskInput, UpdateTenantInput, UpdateUserInput, User, UserFilter, UserListResult, UserNotFoundError, UserWithRoles, ValidationError, VpnConfig, WebhookDeliveryResult, WebhookManager, WifiConfig } from './types.js';
|
|
2
|
+
export { AppInstallationSummary, AppRollback, AppVersion, Application, ApplicationManager, ApplicationNotFoundError, AuditAction, AuditLog, AuditLogFilter, AuditLogListResult, AuditSummary, AuthConfig, AuthenticationError, AuthorizationError, Command, CommandFilter, CommandManager, CommandNotFoundError, CommandResult, CommandStatus, CommandSuccessRates, CommandType, CreateAppRollbackInput, CreateApplicationInput, CreateAuditLogInput, CreateDeviceInput, CreateGroupInput, CreatePolicyInput, CreateRoleInput, CreateScheduledTaskInput, CreateTenantInput, CreateUserInput, DashboardStats, DeployTarget, Device, DeviceFilter, DeviceListResult, DeviceLocation, DeviceManager, DeviceNotFoundError, DeviceStatus, DeviceStatusBreakdown, EnqueueMessageInput, EnrollmentConfig, EnrollmentError, EnrollmentMethod, EnrollmentRequest, EnrollmentResponse, EnrollmentTrendPoint, EventFilter, EventHandler, EventPayloadMap, Group, GroupHierarchyStats, GroupManager, GroupNotFoundError, GroupTreeNode, HardwareControl, Heartbeat, InstalledApp, MDMError, MDMPlugin, MaintenanceWindow, PasswordPolicy, Permission, PermissionAction, PermissionResource, PluginMiddleware, PluginRoute, PluginStorageEntry, Policy, PolicyApplication, PolicyManager, PolicyNotFoundError, PolicySettings, PushAdapter, PushBatchResult, PushConfig, PushMessage, PushProviderConfig, PushResult, PushToken, QueueMessageStatus, QueueStats, QueuedMessage, RegisterPushTokenInput, Role, RoleNotFoundError, ScheduledTask, ScheduledTaskFilter, ScheduledTaskListResult, ScheduledTaskStatus, SendCommandInput, StorageConfig, SystemUpdatePolicy, TaskExecution, TaskSchedule, TaskType, Tenant, TenantFilter, TenantListResult, TenantNotFoundError, TenantSettings, TenantStats, TenantStatus, TimeWindow, UpdateApplicationInput, UpdateDeviceInput, UpdateGroupInput, UpdatePolicyInput, UpdateRoleInput, UpdateScheduledTaskInput, UpdateTenantInput, UpdateUserInput, User, UserFilter, UserListResult, UserNotFoundError, UserWithRoles, ValidationError, VpnConfig, WebhookDeliveryResult, WebhookManager, WifiConfig } from './types.js';
|
|
3
3
|
export { ColumnDefinition, ColumnType, IndexDefinition, SchemaDefinition, TableDefinition, camelToSnake, getColumnNames, getPrimaryKey, getTableNames, mdmSchema, snakeToCamel, transformToCamelCase, transformToSnakeCase } from './schema.js';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* OpenMDM Agent Wire Protocol v2.
|
|
7
|
+
*
|
|
8
|
+
* A unified response envelope for every `/agent/*` endpoint, plus the
|
|
9
|
+
* version-selection rules that let the server serve v1 and v2 clients
|
|
10
|
+
* simultaneously during a fleet rollout.
|
|
11
|
+
*
|
|
12
|
+
* ## Background
|
|
13
|
+
*
|
|
14
|
+
* Until now, agent-facing handlers returned either a bare JSON body
|
|
15
|
+
* on success or raised an `HTTPException(401|404|5xx)` on failure.
|
|
16
|
+
* The agent had to interpret five different HTTP status codes and
|
|
17
|
+
* infer what to do about each — which in practice meant "on auth
|
|
18
|
+
* error, wipe local enrollment state and re-enroll". That single
|
|
19
|
+
* ambiguity produced the auto-unenroll behavior we saw in production:
|
|
20
|
+
* a transient 401 or 404 was indistinguishable from "you are really
|
|
21
|
+
* unenrolled", so the agent self-destructed.
|
|
22
|
+
*
|
|
23
|
+
* ## Protocol v2
|
|
24
|
+
*
|
|
25
|
+
* Every agent-facing endpoint replies with HTTP 200 and a body of
|
|
26
|
+
* shape {@link AgentResponse}:
|
|
27
|
+
*
|
|
28
|
+
* ```json
|
|
29
|
+
* { "ok": true, "action": "none", "data": { ... } }
|
|
30
|
+
* { "ok": false, "action": "retry", "message": "..." }
|
|
31
|
+
* { "ok": false, "action": "reauth", "message": "..." }
|
|
32
|
+
* { "ok": false, "action": "unenroll", "message": "..." }
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* - `ok` is the boolean the agent checks first.
|
|
36
|
+
* - `action` is the *only* field the agent reads to decide what to do
|
|
37
|
+
* next. There is exactly one handler per action on the client, so
|
|
38
|
+
* adding a new server response path is a matter of picking an
|
|
39
|
+
* existing action.
|
|
40
|
+
* - `data` carries the handler-specific payload (heartbeat response,
|
|
41
|
+
* policy update, etc.) on success.
|
|
42
|
+
* - `message` is a human-readable hint, for logs.
|
|
43
|
+
*
|
|
44
|
+
* HTTP 5xx is still used for real infrastructure failures (the Lambda
|
|
45
|
+
* timed out, the database connection dropped, etc.). v2 envelopes are
|
|
46
|
+
* reserved for *application-level* failures the agent can reason about.
|
|
47
|
+
*
|
|
48
|
+
* ## Versioning and rollout
|
|
49
|
+
*
|
|
50
|
+
* The agent opts into v2 by sending the header
|
|
51
|
+
* `X-Openmdm-Protocol: 2` on every request. When absent, the server
|
|
52
|
+
* falls back to the legacy v1 behavior — bare JSON on success,
|
|
53
|
+
* `HTTPException(401|404|…)` on failure — so a fleet still running
|
|
54
|
+
* older APKs keeps working during rollout.
|
|
55
|
+
*
|
|
56
|
+
* After the fleet has been upgraded, v1 can be dropped in a future
|
|
57
|
+
* major release by ignoring the header and always emitting v2.
|
|
58
|
+
*/
|
|
59
|
+
/**
|
|
60
|
+
* Instruction the server gives the agent on how to react to this
|
|
61
|
+
* response. This is the entire client-side decision space.
|
|
62
|
+
*
|
|
63
|
+
* - `none`: happy path. The agent consumes `data` and continues.
|
|
64
|
+
* - `retry`: transient problem. The agent re-tries later without
|
|
65
|
+
* touching local state.
|
|
66
|
+
* - `reauth`: the agent's access token is no longer valid. It should
|
|
67
|
+
* call the refresh flow. It must NOT wipe enrollment state.
|
|
68
|
+
* - `unenroll`: the server-side record for this device is gone or
|
|
69
|
+
* blocked and the agent's credentials will never work again. The
|
|
70
|
+
* agent should stop making requests and surface this to the user.
|
|
71
|
+
* In Phase 2b this will be further softened: the agent will attempt
|
|
72
|
+
* a hardware-identity-based rebind before treating this as terminal.
|
|
73
|
+
*/
|
|
74
|
+
type AgentAction = 'none' | 'retry' | 'reauth' | 'unenroll';
|
|
75
|
+
/**
|
|
76
|
+
* Unified response envelope for every `/agent/*` endpoint under
|
|
77
|
+
* protocol v2.
|
|
78
|
+
*
|
|
79
|
+
* Successful responses carry `data`; failure responses carry
|
|
80
|
+
* `message`. The envelope never carries both the happy-path payload
|
|
81
|
+
* and an error hint at the same time.
|
|
82
|
+
*/
|
|
83
|
+
type AgentResponse<T = unknown> = {
|
|
84
|
+
ok: true;
|
|
85
|
+
action: 'none';
|
|
86
|
+
data: T;
|
|
87
|
+
} | {
|
|
88
|
+
ok: false;
|
|
89
|
+
action: Exclude<AgentAction, 'none'>;
|
|
90
|
+
message?: string;
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* HTTP header an agent sends to opt into protocol v2. Case-insensitive
|
|
94
|
+
* on the wire; use the constant to avoid typos.
|
|
95
|
+
*/
|
|
96
|
+
declare const AGENT_PROTOCOL_HEADER = "X-Openmdm-Protocol";
|
|
97
|
+
/**
|
|
98
|
+
* Current wire-protocol version. Agents that send
|
|
99
|
+
* `X-Openmdm-Protocol: 2` get envelope responses. Absent or older
|
|
100
|
+
* values are served with the legacy flat shape.
|
|
101
|
+
*/
|
|
102
|
+
declare const AGENT_PROTOCOL_V2 = "2";
|
|
103
|
+
/**
|
|
104
|
+
* Helper: build a success envelope.
|
|
105
|
+
*/
|
|
106
|
+
declare function agentOk<T>(data: T): AgentResponse<T>;
|
|
107
|
+
/**
|
|
108
|
+
* Helper: build a failure envelope.
|
|
109
|
+
*/
|
|
110
|
+
declare function agentFail(action: Exclude<AgentAction, 'none'>, message?: string): AgentResponse<never>;
|
|
111
|
+
/**
|
|
112
|
+
* Returns `true` iff the caller should be served protocol v2. The
|
|
113
|
+
* input is the value of the {@link AGENT_PROTOCOL_HEADER} header,
|
|
114
|
+
* which may be undefined.
|
|
115
|
+
*/
|
|
116
|
+
declare function wantsAgentProtocolV2(headerValue: string | undefined | null): boolean;
|
|
117
|
+
|
|
5
118
|
/**
|
|
6
119
|
* OpenMDM Webhook Delivery System
|
|
7
120
|
*
|
|
@@ -189,4 +302,4 @@ declare function parsePluginKey(key: string): {
|
|
|
189
302
|
*/
|
|
190
303
|
declare function createMDM(config: MDMConfig): MDMInstance;
|
|
191
304
|
|
|
192
|
-
export { AuditConfig, AuditManager, AuthorizationManager, DashboardManager, DatabaseAdapter, EventType, MDMConfig, MDMEvent, MDMInstance, MessageQueueManager, PluginStorageAdapter, ScheduleManager, TenantManager, WebhookConfig, WebhookEndpoint, type WebhookPayload, createAuditManager, createAuthorizationManager, createDashboardManager, createMDM, createMemoryPluginStorageAdapter, createMessageQueueManager, createPluginKey, createPluginStorageAdapter, createScheduleManager, createTenantManager, createWebhookManager, parsePluginKey, verifyWebhookSignature };
|
|
305
|
+
export { AGENT_PROTOCOL_HEADER, AGENT_PROTOCOL_V2, type AgentAction, type AgentResponse, AuditConfig, AuditManager, AuthorizationManager, DashboardManager, DatabaseAdapter, EventType, MDMConfig, MDMEvent, MDMInstance, MessageQueueManager, PluginStorageAdapter, ScheduleManager, TenantManager, WebhookConfig, WebhookEndpoint, type WebhookPayload, agentFail, agentOk, createAuditManager, createAuthorizationManager, createDashboardManager, createMDM, createMemoryPluginStorageAdapter, createMessageQueueManager, createPluginKey, createPluginStorageAdapter, createScheduleManager, createTenantManager, createWebhookManager, parsePluginKey, verifyWebhookSignature, wantsAgentProtocolV2 };
|
package/dist/index.js
CHANGED
|
@@ -27,6 +27,11 @@ var ApplicationNotFoundError = class extends MDMError {
|
|
|
27
27
|
super(`Application not found: ${identifier}`, "APPLICATION_NOT_FOUND", 404);
|
|
28
28
|
}
|
|
29
29
|
};
|
|
30
|
+
var CommandNotFoundError = class extends MDMError {
|
|
31
|
+
constructor(commandId) {
|
|
32
|
+
super(`Command not found: ${commandId}`, "COMMAND_NOT_FOUND", 404);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
30
35
|
var TenantNotFoundError = class extends MDMError {
|
|
31
36
|
constructor(identifier) {
|
|
32
37
|
super(`Tenant not found: ${identifier}`, "TENANT_NOT_FOUND", 404);
|
|
@@ -2021,6 +2026,19 @@ function transformToSnakeCase(obj) {
|
|
|
2021
2026
|
return result;
|
|
2022
2027
|
}
|
|
2023
2028
|
|
|
2029
|
+
// src/agent-protocol.ts
|
|
2030
|
+
var AGENT_PROTOCOL_HEADER = "X-Openmdm-Protocol";
|
|
2031
|
+
var AGENT_PROTOCOL_V2 = "2";
|
|
2032
|
+
function agentOk(data) {
|
|
2033
|
+
return { ok: true, action: "none", data };
|
|
2034
|
+
}
|
|
2035
|
+
function agentFail(action, message) {
|
|
2036
|
+
return { ok: false, action, message };
|
|
2037
|
+
}
|
|
2038
|
+
function wantsAgentProtocolV2(headerValue) {
|
|
2039
|
+
return headerValue === AGENT_PROTOCOL_V2;
|
|
2040
|
+
}
|
|
2041
|
+
|
|
2024
2042
|
// src/index.ts
|
|
2025
2043
|
function createMDM(config) {
|
|
2026
2044
|
const { database, push, enrollment, webhooks: webhooksConfig, plugins = [] } = config;
|
|
@@ -2367,13 +2385,20 @@ function createMDM(config) {
|
|
|
2367
2385
|
});
|
|
2368
2386
|
},
|
|
2369
2387
|
async cancel(id) {
|
|
2370
|
-
|
|
2388
|
+
const command = await database.updateCommand(id, { status: "cancelled" });
|
|
2389
|
+
if (!command) {
|
|
2390
|
+
throw new CommandNotFoundError(id);
|
|
2391
|
+
}
|
|
2392
|
+
return command;
|
|
2371
2393
|
},
|
|
2372
2394
|
async acknowledge(id) {
|
|
2373
2395
|
const command = await database.updateCommand(id, {
|
|
2374
2396
|
status: "acknowledged",
|
|
2375
2397
|
acknowledgedAt: /* @__PURE__ */ new Date()
|
|
2376
2398
|
});
|
|
2399
|
+
if (!command) {
|
|
2400
|
+
throw new CommandNotFoundError(id);
|
|
2401
|
+
}
|
|
2377
2402
|
const device = await database.findDevice(command.deviceId);
|
|
2378
2403
|
if (device) {
|
|
2379
2404
|
await emit("command.acknowledged", { device, command });
|
|
@@ -2386,6 +2411,9 @@ function createMDM(config) {
|
|
|
2386
2411
|
result,
|
|
2387
2412
|
completedAt: /* @__PURE__ */ new Date()
|
|
2388
2413
|
});
|
|
2414
|
+
if (!command) {
|
|
2415
|
+
throw new CommandNotFoundError(id);
|
|
2416
|
+
}
|
|
2389
2417
|
const device = await database.findDevice(command.deviceId);
|
|
2390
2418
|
if (device) {
|
|
2391
2419
|
await emit("command.completed", { device, command, result });
|
|
@@ -2398,6 +2426,9 @@ function createMDM(config) {
|
|
|
2398
2426
|
error,
|
|
2399
2427
|
completedAt: /* @__PURE__ */ new Date()
|
|
2400
2428
|
});
|
|
2429
|
+
if (!command) {
|
|
2430
|
+
throw new CommandNotFoundError(id);
|
|
2431
|
+
}
|
|
2401
2432
|
const device = await database.findDevice(command.deviceId);
|
|
2402
2433
|
if (device) {
|
|
2403
2434
|
await emit("command.failed", { device, command, error });
|
|
@@ -2877,6 +2908,6 @@ function generateDeviceToken(deviceId, secret, expirationSeconds) {
|
|
|
2877
2908
|
return `${header}.${payload}.${signature}`;
|
|
2878
2909
|
}
|
|
2879
2910
|
|
|
2880
|
-
export { ApplicationNotFoundError, AuthenticationError, AuthorizationError, DeviceNotFoundError, EnrollmentError, GroupNotFoundError, MDMError, PolicyNotFoundError, RoleNotFoundError, TenantNotFoundError, UserNotFoundError, ValidationError, camelToSnake, createAuditManager, createAuthorizationManager, createDashboardManager, createMDM, createMemoryPluginStorageAdapter, createMessageQueueManager, createPluginKey, createPluginStorageAdapter, createScheduleManager, createTenantManager, createWebhookManager, getColumnNames, getPrimaryKey, getTableNames, mdmSchema, parsePluginKey, snakeToCamel, transformToCamelCase, transformToSnakeCase, verifyWebhookSignature };
|
|
2911
|
+
export { AGENT_PROTOCOL_HEADER, AGENT_PROTOCOL_V2, ApplicationNotFoundError, AuthenticationError, AuthorizationError, CommandNotFoundError, DeviceNotFoundError, EnrollmentError, GroupNotFoundError, MDMError, PolicyNotFoundError, RoleNotFoundError, TenantNotFoundError, UserNotFoundError, ValidationError, agentFail, agentOk, camelToSnake, createAuditManager, createAuthorizationManager, createDashboardManager, createMDM, createMemoryPluginStorageAdapter, createMessageQueueManager, createPluginKey, createPluginStorageAdapter, createScheduleManager, createTenantManager, createWebhookManager, getColumnNames, getPrimaryKey, getTableNames, mdmSchema, parsePluginKey, snakeToCamel, transformToCamelCase, transformToSnakeCase, verifyWebhookSignature, wantsAgentProtocolV2 };
|
|
2881
2912
|
//# sourceMappingURL=index.js.map
|
|
2882
2913
|
//# sourceMappingURL=index.js.map
|