check_cloud_sync_status
check_cloud_sync_status
Reports the current sync state of installed cloud sync clients (OneDrive, iCloud Drive, Google Drive, Dropbox) — per-client install state, last-sync timestamp, queue depth where surfaced, and a top-level stale flag set when any client's lastSync is older than the threshold (default 24h). Used both by the user-facing Cloud Sync repair skill and as the telemetry source for the proactive cloud-sync-stale trigger.
Metadata
Name
check_cloud_sync_status
Updated
2 months ago
Source
Risk
Low
Requires consent
false
Affected scope
user
Code
/**
* mcp/skills/checkCloudSyncStatus.ts — check_cloud_sync_status skill
*
* Probes the on-disk state of cloud sync clients (OneDrive, iCloud Drive,
* Google Drive, Dropbox) and reports per-tick a `stale` boolean +
* supporting context that the proactive Trigger 4 (`cloud-sync-stale`)
* evaluates on a steady schedule.
*
* Telemetry contract (Track B Phase 4)
* ------------------------------------
* `meta.outputKeys` includes: platform, client, lastSyncMs, queueDepth,
* status, stale. These are at the **top level** of the result so the
* proactive evaluator's restricted DSL can reference them. The DSL
* never sees the per-client breakdown in `clients[]`.
*
* Default mode (`client: "auto"`) picks the most-stale installed
* client, biased toward producing a `stale: true` observation when any
* sync is overdue — that's what the rule wants to detect. Caller can
* pass `client: "onedrive"` etc. to inspect a specific client.
*
* Read-only — sync state mutation lives in `pause_resume_cloud_sync`.
*/
import * as os from "os";
import * as path from "path";
import { promises as fs } from "fs";
import { z } from "zod";
// -- Meta ---------------------------------------------------------------------
export const meta = {
name: "check_cloud_sync_status",
description:
"Reports the current sync state of installed cloud sync clients " +
"(OneDrive, iCloud Drive, Google Drive, Dropbox) — per-client install " +
"state, last-sync timestamp, queue depth where surfaced, and a top-level " +
"`stale` flag set when any client's lastSync is older than the threshold " +
"(default 24h). Used both by the user-facing Cloud Sync repair skill and " +
"as the telemetry source for the proactive cloud-sync-stale trigger.",
riskLevel: "low",
destructive: false,
requiresConsent: false,
supportsDryRun: false,
affectedScope: ["user"],
auditRequired: false,
tccCategories: ["FullDiskAccess"],
outputKeys: [
"platform",
"client",
"lastSyncMs",
"queueDepth",
"status",
"stale",
],
schema: {
client: z
.enum(["onedrive", "icloud", "google-drive", "dropbox", "auto"])
.optional()
.describe(
"Which client's status to report at the top level. Default 'auto' " +
"picks the most-stale installed client (the one that should fire " +
"a proactive trigger if any do). Pass an explicit client to inspect " +
"one specific sync.",
),
staleThresholdHours: z
.number()
.int()
.min(1)
.max(168)
.optional()
.describe("Hours since lastSync that count as stale. Default 24."),
},
} as const;
// -- Types --------------------------------------------------------------------
export type SyncClient = "onedrive" | "icloud" | "google-drive" | "dropbox";
export type SyncStatus =
| "syncing" // active sync in progress
| "idle" // fresh; nothing to do
| "stale" // hasn't synced in > threshold
| "error" // surfaced error from the client
| "not-installed"
| "unknown";
export interface SyncClientInfo {
client: SyncClient;
installed: boolean;
/** Detected installation path (app bundle on darwin, directory on win32). */
installPath: string | null;
/** ms since epoch — null when no probe path was readable. */
lastSyncMs: number | null;
/** Number of items pending sync — null when not surfaced. */
queueDepth: number | null;
status: SyncStatus;
stale: boolean;
}
export interface CheckCloudSyncStatusResult {
platform: NodeJS.Platform;
/** Top-level summary fields — duplicated from the chosen client (auto = most stale). */
client: SyncClient | null;
lastSyncMs: number | null;
queueDepth: number | null;
status: SyncStatus;
stale: boolean;
/** Per-client breakdown for the skill's manual flow. */
clients: SyncClientInfo[];
}
const ALL_CLIENTS: SyncClient[] = ["onedrive", "icloud", "google-drive", "dropbox"];
const DEFAULT_STALE_HOURS = 24;
// -- Per-client probe paths ---------------------------------------------------
interface ProbePath {
installPaths: string[]; // existence of any one means "installed"
syncProbePaths: string[]; // mtime of newest existing path → lastSyncMs
}
function probePathsFor(client: SyncClient, platform: NodeJS.Platform): ProbePath {
const home = os.homedir();
const appData = process.env.APPDATA ?? path.join(home, "AppData", "Roaming");
const local = process.env.LOCALAPPDATA ?? path.join(home, "AppData", "Local");
switch (client) {
case "onedrive":
if (platform === "darwin") {
return {
installPaths: ["/Applications/OneDrive.app"],
syncProbePaths: [
path.join(home, "Library", "Logs", "OneDrive"),
path.join(home, "Library", "Application Support", "OneDrive"),
path.join(home, "Library", "Containers", "com.microsoft.OneDrive-mac"),
],
};
}
return {
installPaths: [
path.join(local, "Microsoft", "OneDrive"),
"C:\Program Files\Microsoft OneDrive",
"C:\Program Files (x86)\Microsoft OneDrive",
],
syncProbePaths: [
path.join(local, "Microsoft", "OneDrive", "logs"),
path.join(local, "Microsoft", "OneDrive", "settings"),
],
};
case "icloud":
if (platform === "darwin") {
return {
installPaths: [
path.join(home, "Library", "Application Support", "CloudDocs"),
path.join(home, "Library", "Mobile Documents"),
],
syncProbePaths: [
path.join(home, "Library", "Application Support", "CloudDocs", "session", "db"),
path.join(home, "Library", "Mobile Documents"),
],
};
}
return {
installPaths: [
path.join(local, "Apple Inc", "iCloud"),
path.join(home, "iCloudDrive"),
],
syncProbePaths: [
path.join(home, "iCloudDrive"),
],
};
case "google-drive":
if (platform === "darwin") {
return {
installPaths: ["/Applications/Google Drive.app"],
syncProbePaths: [
path.join(home, "Library", "Application Support", "Google", "DriveFS", "Logs"),
path.join(home, "Library", "Application Support", "Google", "DriveFS"),
],
};
}
return {
installPaths: [
path.join(local, "Google", "DriveFS"),
"C:\Program Files\Google\Drive File Stream",
],
syncProbePaths: [
path.join(local, "Google", "DriveFS", "Logs"),
path.join(local, "Google", "DriveFS"),
],
};
case "dropbox":
if (platform === "darwin") {
return {
installPaths: [
"/Applications/Dropbox.app",
path.join(home, "Library", "Application Support", "Dropbox"),
],
syncProbePaths: [
path.join(home, "Library", "Application Support", "Dropbox", "instance1", "logs"),
path.join(home, "Library", "Application Support", "Dropbox", "info.json"),
],
};
}
return {
installPaths: [
path.join(appData, "Dropbox"),
path.join(local, "Dropbox"),
],
syncProbePaths: [
path.join(appData, "Dropbox", "info.json"),
path.join(appData, "Dropbox", "instance1", "logs"),
],
};
}
}
// -- Filesystem helpers -------------------------------------------------------
async function pathExists(p: string): Promise<boolean> {
try {
await fs.access(p);
return true;
} catch {
return false;
}
}
async function newestMtimeMs(paths: string[]): Promise<number | null> {
let newest: number | null = null;
for (const p of paths) {
try {
const s = await fs.stat(p);
if (newest === null || s.mtimeMs > newest) newest = s.mtimeMs;
} catch {
// Path missing — skip.
}
}
return newest;
}
async function firstExistingPath(candidates: string[]): Promise<string | null> {
for (const c of candidates) {
if (await pathExists(c)) return c;
}
return null;
}
// -- Per-client status computation --------------------------------------------
function computeStatus(lastSyncMs: number | null, staleThresholdMs: number): { status: SyncStatus; stale: boolean } {
if (lastSyncMs === null) return { status: "unknown", stale: false };
const ageMs = Date.now() - lastSyncMs;
if (ageMs >= staleThresholdMs) return { status: "stale", stale: true };
return { status: "idle", stale: false };
}
async function probeOne(
client: SyncClient,
platform: NodeJS.Platform,
staleThresholdMs: number,
): Promise<SyncClientInfo> {
const probe = probePathsFor(client, platform);
const installPath = await firstExistingPath(probe.installPaths);
if (!installPath) {
return {
client,
installed: false,
installPath: null,
lastSyncMs: null,
queueDepth: null,
status: "not-installed",
stale: false,
};
}
const lastSyncMs = await newestMtimeMs(probe.syncProbePaths);
const { status, stale } = computeStatus(lastSyncMs, staleThresholdMs);
return {
client,
installed: true,
installPath,
lastSyncMs,
queueDepth: null,
status,
stale,
};
}
// -- Top-level summary picker -------------------------------------------------
function pickTopLevel(
clients: SyncClientInfo[],
prefer: SyncClient | "auto",
): SyncClientInfo | null {
if (prefer !== "auto") return clients.find((c) => c.client === prefer) ?? null;
const installed = clients.filter((c) => c.installed);
if (installed.length === 0) return null;
const stale = installed.filter((c) => c.stale);
if (stale.length > 0) {
return stale.reduce((oldest, c) =>
(c.lastSyncMs ?? Infinity) < (oldest.lastSyncMs ?? Infinity) ? c : oldest);
}
return installed.reduce((newest, c) =>
(c.lastSyncMs ?? 0) > (newest.lastSyncMs ?? 0) ? c : newest);
}
// -- Exported run function ----------------------------------------------------
export async function run({
client = "auto",
staleThresholdHours = DEFAULT_STALE_HOURS,
}: {
client?: "onedrive" | "icloud" | "google-drive" | "dropbox" | "auto";
staleThresholdHours?: number;
} = {}): Promise<CheckCloudSyncStatusResult> {
const platform = os.platform();
if (platform !== "darwin" && platform !== "win32") {
throw new Error(`check_cloud_sync_status: unsupported platform "${platform}"`);
}
const staleThresholdMs = staleThresholdHours * 60 * 60 * 1000;
const clients = await Promise.all(ALL_CLIENTS.map((c) => probeOne(c, platform, staleThresholdMs)));
const top = pickTopLevel(clients, client);
return {
platform,
client: top?.client ?? null,
lastSyncMs: top?.lastSyncMs ?? null,
queueDepth: top?.queueDepth ?? null,
status: top?.status ?? "not-installed",
stale: top?.stale ?? false,
clients,
};
}
// -- Test helpers -------------------------------------------------------------
/** Exported for unit tests only — do not use from production code. */
export const __testing = {
ALL_CLIENTS,
probePathsFor,
pathExists,
newestMtimeMs,
firstExistingPath,
computeStatus,
probeOne,
pickTopLevel,
};
``