@hasna/machines 0.0.37 → 0.0.38

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.
@@ -0,0 +1,76 @@
1
+ export type DaemonServicePlatform = "macos" | "linux";
2
+ export type DaemonServiceMode = "user" | "system";
3
+ export type DaemonServiceAction = "install" | "uninstall" | "restart" | "status" | "logs";
4
+ export interface DaemonServiceOptions {
5
+ action: DaemonServiceAction;
6
+ platform?: DaemonServicePlatform | "darwin";
7
+ mode?: DaemonServiceMode;
8
+ serviceName?: string;
9
+ executable?: string;
10
+ intervalMs?: number;
11
+ storagePush?: boolean;
12
+ doctorSummary?: boolean;
13
+ privateMetadata?: boolean | readonly string[];
14
+ env?: readonly string[];
15
+ }
16
+ export interface DaemonServiceCommand {
17
+ id: string;
18
+ description: string;
19
+ program: string;
20
+ args: string[];
21
+ sudo: boolean;
22
+ mutates: boolean;
23
+ allowFailure?: boolean;
24
+ env?: Record<string, string>;
25
+ }
26
+ export interface DaemonServiceFile {
27
+ id: string;
28
+ description: string;
29
+ path: string;
30
+ mode: string;
31
+ content: string;
32
+ }
33
+ export interface DaemonServicePlan {
34
+ platform: DaemonServicePlatform;
35
+ mode: DaemonServiceMode;
36
+ action: DaemonServiceAction;
37
+ serviceName: string;
38
+ serviceId: string;
39
+ executable: string;
40
+ intervalMs: number;
41
+ commands: DaemonServiceCommand[];
42
+ files: DaemonServiceFile[];
43
+ warnings: string[];
44
+ manualSteps: string[];
45
+ }
46
+ export interface DaemonServiceRunOptions {
47
+ apply?: boolean;
48
+ yes?: boolean;
49
+ }
50
+ export interface DaemonServiceCommandResult {
51
+ id: string;
52
+ command: string[];
53
+ skipped: boolean;
54
+ exitCode: number | null;
55
+ stdout: string;
56
+ stderr: string;
57
+ error?: string;
58
+ }
59
+ export interface DaemonServiceRunResult {
60
+ mode: "plan" | "apply";
61
+ applied: boolean;
62
+ plan: DaemonServicePlan;
63
+ filesWritten: string[];
64
+ commands: DaemonServiceCommandResult[];
65
+ warnings: string[];
66
+ }
67
+ export declare function buildDaemonServicePlan(options: DaemonServiceOptions): DaemonServicePlan;
68
+ export declare function runDaemonServicePlan(plan: DaemonServicePlan, options?: DaemonServiceRunOptions): DaemonServiceRunResult;
69
+ export declare function buildDaemonInstallPlan(options?: Omit<DaemonServiceOptions, "action">): DaemonServicePlan;
70
+ export declare function buildDaemonUninstallPlan(options?: Omit<DaemonServiceOptions, "action">): DaemonServicePlan;
71
+ export declare function buildDaemonRestartPlan(options?: Omit<DaemonServiceOptions, "action">): DaemonServicePlan;
72
+ export declare function buildDaemonStatusPlan(options?: Omit<DaemonServiceOptions, "action">): DaemonServicePlan;
73
+ export declare function buildDaemonLogsPlan(options?: Omit<DaemonServiceOptions, "action">): DaemonServicePlan;
74
+ export declare function renderLaunchdPlist(options?: Omit<DaemonServiceOptions, "action">): string;
75
+ export declare function renderSystemdUnit(options?: Omit<DaemonServiceOptions, "action">): string;
76
+ //# sourceMappingURL=daemon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../../src/commands/daemon.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/commands/serve.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAWD,wBAAgB,YAAY,CAAC,OAAO,GAAE,YAAiB,GAAG,SAAS,CAyBlE;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CA+J5C;AAcD,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,YAAiB,GAAG,UAAU,CAAC,OAAO,GAAG,CAAC,KAAK,CAAC,CA6H7F"}
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/commands/serve.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAWD,wBAAgB,YAAY,CAAC,OAAO,GAAE,YAAiB,GAAG,SAAS,CA4BlE;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CA6K5C;AAuBD,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,YAAiB,GAAG,UAAU,CAAC,OAAO,GAAG,CAAC,KAAK,CAAC,CAmJ7F"}
@@ -1 +1 @@
1
- {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,wBAAgB,SAAS,IAAI,WAAW,CA8BvC"}
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAY/C,wBAAgB,SAAS,IAAI,WAAW,CAmCvC"}
package/dist/consumer.js CHANGED
@@ -102,6 +102,21 @@ class SqliteAdapter {
102
102
  }
103
103
  }
104
104
  var adapter = null;
105
+ var AGENT_HEARTBEAT_COLUMNS = [
106
+ { name: "daemon_version", definition: "TEXT" },
107
+ { name: "agent_mode", definition: "TEXT" },
108
+ { name: "platform", definition: "TEXT" },
109
+ { name: "os_version", definition: "TEXT" },
110
+ { name: "os_build", definition: "TEXT" },
111
+ { name: "arch", definition: "TEXT" },
112
+ { name: "uptime_seconds", definition: "INTEGER" },
113
+ { name: "tool_versions_json", definition: "TEXT" },
114
+ { name: "tailscale_json", definition: "TEXT" },
115
+ { name: "storage_sync_status", definition: "TEXT" },
116
+ { name: "storage_sync_last_error", definition: "TEXT" },
117
+ { name: "doctor_summary_json", definition: "TEXT" },
118
+ { name: "private_metadata", definition: "INTEGER NOT NULL DEFAULT 0" }
119
+ ];
105
120
  function createTables(db) {
106
121
  db.exec(`
107
122
  CREATE TABLE IF NOT EXISTS agent_heartbeats (
@@ -109,9 +124,23 @@ function createTables(db) {
109
124
  pid INTEGER NOT NULL,
110
125
  status TEXT NOT NULL,
111
126
  updated_at TEXT NOT NULL,
127
+ daemon_version TEXT,
128
+ agent_mode TEXT,
129
+ platform TEXT,
130
+ os_version TEXT,
131
+ os_build TEXT,
132
+ arch TEXT,
133
+ uptime_seconds INTEGER,
134
+ tool_versions_json TEXT,
135
+ tailscale_json TEXT,
136
+ storage_sync_status TEXT,
137
+ storage_sync_last_error TEXT,
138
+ doctor_summary_json TEXT,
139
+ private_metadata INTEGER NOT NULL DEFAULT 0,
112
140
  PRIMARY KEY (machine_id, pid)
113
141
  )
114
142
  `);
143
+ migrateAgentHeartbeats(db);
115
144
  db.exec(`
116
145
  CREATE TABLE IF NOT EXISTS setup_runs (
117
146
  id TEXT PRIMARY KEY,
@@ -133,6 +162,15 @@ function createTables(db) {
133
162
  )
134
163
  `);
135
164
  }
165
+ function migrateAgentHeartbeats(db) {
166
+ const columns = db.query("PRAGMA table_info(agent_heartbeats)").all();
167
+ const existing = new Set(columns.map((column) => column.name));
168
+ for (const column of AGENT_HEARTBEAT_COLUMNS) {
169
+ if (existing.has(column.name))
170
+ continue;
171
+ db.exec(`ALTER TABLE agent_heartbeats ADD COLUMN ${column.name} ${column.definition}`);
172
+ }
173
+ }
136
174
  function getAdapter(path = getDbPath()) {
137
175
  if (path === ":memory:") {
138
176
  const memoryAdapter = new SqliteAdapter(path);
@@ -161,13 +199,44 @@ function closeDb() {
161
199
  adapter = null;
162
200
  }
163
201
  }
164
- function upsertHeartbeat(machineId, pid = process.pid, status = "online") {
202
+ function upsertHeartbeat(machineId, pid = process.pid, status = "online", metadata = {}) {
165
203
  const db = getDb();
166
- db.query(`INSERT INTO agent_heartbeats (machine_id, pid, status, updated_at)
167
- VALUES (?, ?, ?, ?)
204
+ db.query(`INSERT INTO agent_heartbeats (
205
+ machine_id,
206
+ pid,
207
+ status,
208
+ updated_at,
209
+ daemon_version,
210
+ agent_mode,
211
+ platform,
212
+ os_version,
213
+ os_build,
214
+ arch,
215
+ uptime_seconds,
216
+ tool_versions_json,
217
+ tailscale_json,
218
+ storage_sync_status,
219
+ storage_sync_last_error,
220
+ doctor_summary_json,
221
+ private_metadata
222
+ )
223
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
168
224
  ON CONFLICT(machine_id, pid) DO UPDATE SET
169
225
  status = excluded.status,
170
- updated_at = excluded.updated_at`).run(machineId, pid, status, new Date().toISOString());
226
+ updated_at = excluded.updated_at,
227
+ daemon_version = excluded.daemon_version,
228
+ agent_mode = excluded.agent_mode,
229
+ platform = excluded.platform,
230
+ os_version = excluded.os_version,
231
+ os_build = excluded.os_build,
232
+ arch = excluded.arch,
233
+ uptime_seconds = excluded.uptime_seconds,
234
+ tool_versions_json = excluded.tool_versions_json,
235
+ tailscale_json = excluded.tailscale_json,
236
+ storage_sync_status = excluded.storage_sync_status,
237
+ storage_sync_last_error = excluded.storage_sync_last_error,
238
+ doctor_summary_json = excluded.doctor_summary_json,
239
+ private_metadata = excluded.private_metadata`).run(machineId, pid, status, new Date().toISOString(), metadata.daemonVersion ?? null, metadata.agentMode ?? null, metadata.platform ?? null, metadata.osVersion ?? null, metadata.osBuild ?? null, metadata.arch ?? null, metadata.uptimeSeconds == null ? null : Math.max(0, Math.floor(metadata.uptimeSeconds)), metadata.toolVersions ? JSON.stringify(metadata.toolVersions) : null, metadata.tailscale ? JSON.stringify(metadata.tailscale) : null, metadata.storageSyncStatus ?? null, metadata.storageSyncLastError ?? null, metadata.doctorSummary ? JSON.stringify(metadata.doctorSummary) : null, metadata.privateMetadata ? 1 : 0);
171
240
  }
172
241
  function getLocalMachineId() {
173
242
  return process.env["HASNA_MACHINES_MACHINE_ID"] || hostname();
@@ -175,12 +244,12 @@ function getLocalMachineId() {
175
244
  function listHeartbeats(machineId) {
176
245
  const db = getDb();
177
246
  if (machineId) {
178
- return db.query(`SELECT machine_id, pid, status, updated_at
247
+ return db.query(`SELECT *
179
248
  FROM agent_heartbeats
180
249
  WHERE machine_id = ?
181
250
  ORDER BY updated_at DESC`).all(machineId);
182
251
  }
183
- return db.query(`SELECT machine_id, pid, status, updated_at
252
+ return db.query(`SELECT *
184
253
  FROM agent_heartbeats
185
254
  ORDER BY updated_at DESC`).all();
186
255
  }
@@ -4188,6 +4257,11 @@ var coerce = {
4188
4257
  var NEVER = INVALID;
4189
4258
  // src/redaction.ts
4190
4259
  var REDACTED_VALUE = "[redacted]";
4260
+ var PRIVATE_METADATA_ENV = "HASNA_MACHINES_PRIVATE_METADATA";
4261
+ var PRIVATE_METADATA_FALLBACK_ENV = "MACHINES_PRIVATE_METADATA";
4262
+ var PRIVATE_OUTPUT_ENV = "HASNA_MACHINES_ALLOW_PRIVATE_OUTPUT";
4263
+ var PRIVATE_OUTPUT_FALLBACK_ENV = "MACHINES_ALLOW_PRIVATE_OUTPUT";
4264
+ var PRIVATE_OUTPUT_DENIED_WARNING = `private_output_denied:set ${PRIVATE_OUTPUT_ENV}=1 to allow private metadata output`;
4191
4265
  var SENSITIVE_KEY_PATTERN = /(password|passwd|token|credential|private[_-]?key|privateKey|api[_-]?key|github.*key|pem|secret)/i;
4192
4266
  var SECRET_REFERENCE_KEY_PATTERN = /(secret(ref(erence)?|key)?|secretRef|secretKey)$/i;
4193
4267
  var SENSITIVE_VALUE_PATTERNS = [
@@ -4198,9 +4272,21 @@ var SENSITIVE_VALUE_PATTERNS = [
4198
4272
  /\bAKIA[0-9A-Z]{16}\b/,
4199
4273
  /\bsk-[A-Za-z0-9_-]{20,}\b/
4200
4274
  ];
4275
+ var IPV4_PATTERN = /\b(?:10|127|169\.254|172\.(?:1[6-9]|2\d|3[0-1])|192\.168|100\.(?:6[4-9]|[7-9]\d|1[01]\d|12[0-7]))(?:\.\d{1,3}){2}\b/g;
4276
+ var IPV6_PATTERN = /\b(?:fc|fd|fe80)[0-9a-f:]*:[0-9a-f:]+\b/gi;
4277
+ var DATABASE_URL_PATTERN = /\b(?:postgres(?:ql)?|mysql|mariadb|redis|mongodb|s3):\/\/[^\s"'<>]+/gi;
4278
+ var PRIVATE_HOST_PATTERN = /\b(?:[A-Za-z0-9._%+-]+@)?[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)*(?:\.tailnet(?:\.[A-Za-z0-9-]+)*|\.ts\.net|\.private(?:\.[A-Za-z0-9-]+)*|\.internal|\.local)\b/gi;
4201
4279
  function isSensitiveKey(key) {
4202
4280
  return SENSITIVE_KEY_PATTERN.test(key);
4203
4281
  }
4282
+ function isPrivateMetadataEnabled(env = process.env) {
4283
+ const value = env[PRIVATE_METADATA_ENV] ?? env[PRIVATE_METADATA_FALLBACK_ENV];
4284
+ return ["1", "true", "yes", "on", "private"].includes(String(value ?? "").trim().toLowerCase());
4285
+ }
4286
+ function isPrivateOutputEnabled(env = process.env) {
4287
+ const value = env[PRIVATE_OUTPUT_ENV] ?? env[PRIVATE_OUTPUT_FALLBACK_ENV];
4288
+ return ["1", "true", "yes", "on", "private"].includes(String(value ?? "").trim().toLowerCase());
4289
+ }
4204
4290
  function isSecretReferenceKey(key) {
4205
4291
  return SECRET_REFERENCE_KEY_PATTERN.test(key);
4206
4292
  }
@@ -4213,6 +4299,17 @@ function isRecord(value) {
4213
4299
  function redactPath(value) {
4214
4300
  return value.replace(/\/home\/[^/\s]+/g, "/home/<user>").replace(/\/Users\/[^/\s]+/g, "/Users/<user>").replace(/[A-Za-z]:\\Users\\[^\\\s]+/g, "C:\\Users\\<user>");
4215
4301
  }
4302
+ function redactNetworkValue(value) {
4303
+ if (!value.trim())
4304
+ return value;
4305
+ return REDACTED_VALUE;
4306
+ }
4307
+ function redactErrorMessage(value) {
4308
+ return redactPath(value).replace(DATABASE_URL_PATTERN, (match) => {
4309
+ const scheme = match.match(/^([a-z][a-z0-9+.-]*:\/\/)/i)?.[1] ?? "";
4310
+ return `${scheme}${REDACTED_VALUE}`;
4311
+ }).replace(IPV4_PATTERN, REDACTED_VALUE).replace(IPV6_PATTERN, REDACTED_VALUE).replace(PRIVATE_HOST_PATTERN, REDACTED_VALUE);
4312
+ }
4216
4313
  function redactPrivateRef(value) {
4217
4314
  const trimmed = value.trim();
4218
4315
  const scheme = trimmed.match(/^([a-z][a-z0-9+.-]*:\/\/)/i);
@@ -4718,6 +4815,16 @@ function routeRank(hint) {
4718
4815
  function selectRouteHint(hints) {
4719
4816
  return [...hints].sort((left, right) => routeRank(left) - routeRank(right))[0] ?? null;
4720
4817
  }
4818
+ function parseHeartbeatJson(value) {
4819
+ if (!value)
4820
+ return null;
4821
+ try {
4822
+ const parsed = JSON.parse(value);
4823
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
4824
+ } catch {
4825
+ return null;
4826
+ }
4827
+ }
4721
4828
  function buildEntry(input) {
4722
4829
  const manifest = input.manifest;
4723
4830
  const peer = input.peer;
@@ -4740,6 +4847,22 @@ function buildEntry(input) {
4740
4847
  manifest_declared: Boolean(manifest),
4741
4848
  heartbeat_status: input.heartbeat?.status ?? "unknown",
4742
4849
  last_heartbeat_at: input.heartbeat?.updated_at ?? null,
4850
+ agent: {
4851
+ pid: input.heartbeat?.pid ?? null,
4852
+ daemon_version: input.heartbeat?.daemon_version ?? null,
4853
+ mode: input.heartbeat?.agent_mode ?? null,
4854
+ private_metadata: Boolean(input.heartbeat?.private_metadata),
4855
+ platform: input.heartbeat?.platform ?? null,
4856
+ os_version: input.heartbeat?.os_version ?? null,
4857
+ os_build: input.heartbeat?.os_build ?? null,
4858
+ arch: input.heartbeat?.arch ?? null,
4859
+ uptime_seconds: input.heartbeat?.uptime_seconds ?? null,
4860
+ tool_versions: parseHeartbeatJson(input.heartbeat?.tool_versions_json),
4861
+ tailscale: parseHeartbeatJson(input.heartbeat?.tailscale_json),
4862
+ storage_sync_status: input.heartbeat?.storage_sync_status ?? null,
4863
+ storage_sync_last_error: input.heartbeat?.storage_sync_last_error ?? null,
4864
+ doctor_summary: parseHeartbeatJson(input.heartbeat?.doctor_summary_json)
4865
+ },
4743
4866
  tailscale: {
4744
4867
  dns_name: manifest?.tailscaleName ?? peer?.DNSName?.replace(/\.$/, "") ?? null,
4745
4868
  ips: peer?.TailscaleIPs ?? [],
@@ -4799,6 +4922,72 @@ function discoverMachineTopology(options = {}) {
4799
4922
  warnings
4800
4923
  };
4801
4924
  }
4925
+ function redactFleetString(value) {
4926
+ if (!value)
4927
+ return value;
4928
+ return redactErrorMessage(value);
4929
+ }
4930
+ function redactPublicRecord(value) {
4931
+ if (!value)
4932
+ return null;
4933
+ const redacted = {};
4934
+ for (const [key, entry] of Object.entries(value)) {
4935
+ if (/(host|hostname|dns|ip|ips|user|username|serial|address|target|url|token|secret|password|credential)/i.test(key)) {
4936
+ redacted[key] = REDACTED_VALUE;
4937
+ continue;
4938
+ }
4939
+ if (typeof entry === "string") {
4940
+ redacted[key] = redactFleetString(entry);
4941
+ } else if (Array.isArray(entry)) {
4942
+ redacted[key] = entry.map((item) => {
4943
+ if (typeof item === "string")
4944
+ return redactFleetString(item);
4945
+ if (item && typeof item === "object")
4946
+ return redactPublicRecord(item);
4947
+ return item;
4948
+ });
4949
+ } else if (entry && typeof entry === "object") {
4950
+ redacted[key] = redactPublicRecord(entry);
4951
+ } else {
4952
+ redacted[key] = entry;
4953
+ }
4954
+ }
4955
+ return redactSensitiveValue(redacted);
4956
+ }
4957
+ function redactTopologyForOutput(topology, options = {}) {
4958
+ if (options.privateMetadata)
4959
+ return topology;
4960
+ return {
4961
+ ...topology,
4962
+ local_hostname: REDACTED_VALUE,
4963
+ warnings: topology.warnings.map(redactFleetString),
4964
+ machines: topology.machines.map((machine) => ({
4965
+ ...machine,
4966
+ hostname: machine.hostname ? REDACTED_VALUE : null,
4967
+ user: machine.user ? REDACTED_VALUE : null,
4968
+ tailscale: {
4969
+ ...machine.tailscale,
4970
+ dns_name: machine.tailscale.dns_name ? REDACTED_VALUE : null,
4971
+ ips: machine.tailscale.ips.map(() => REDACTED_VALUE)
4972
+ },
4973
+ ssh: {
4974
+ ...machine.ssh,
4975
+ address: machine.ssh.address ? REDACTED_VALUE : null,
4976
+ command_target: machine.ssh.command_target ? REDACTED_VALUE : null
4977
+ },
4978
+ route_hints: machine.route_hints.map((hint) => ({
4979
+ ...hint,
4980
+ target: REDACTED_VALUE
4981
+ })),
4982
+ agent: {
4983
+ ...machine.agent,
4984
+ tailscale: redactPublicRecord(machine.agent.tailscale),
4985
+ storage_sync_last_error: machine.agent.storage_sync_last_error ? redactFleetString(machine.agent.storage_sync_last_error) : null,
4986
+ doctor_summary: redactPublicRecord(machine.agent.doctor_summary)
4987
+ }
4988
+ }))
4989
+ };
4990
+ }
4802
4991
  function normalizeMachineAlias(value) {
4803
4992
  return value.trim().replace(/\.$/, "").toLowerCase();
4804
4993
  }
@@ -5023,6 +5212,20 @@ function resolveMachineRoute(machineId, options = {}) {
5023
5212
  warnings
5024
5213
  };
5025
5214
  }
5215
+ function redactRouteForOutput(route, options = {}) {
5216
+ if (options.privateMetadata)
5217
+ return route;
5218
+ return {
5219
+ ...route,
5220
+ target: route.target ? REDACTED_VALUE : null,
5221
+ command_target: route.command_target ? REDACTED_VALUE : null,
5222
+ warnings: route.warnings.map(redactFleetString),
5223
+ evidence: {
5224
+ ...route.evidence,
5225
+ selected_hint: route.evidence.selected_hint ? { ...route.evidence.selected_hint, target: REDACTED_VALUE } : null
5226
+ }
5227
+ };
5228
+ }
5026
5229
  function isRecord2(value) {
5027
5230
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
5028
5231
  }
@@ -5522,6 +5725,22 @@ function getLocalMachineTopology(options = {}) {
5522
5725
  manifest_declared: false,
5523
5726
  heartbeat_status: "unknown",
5524
5727
  last_heartbeat_at: null,
5728
+ agent: {
5729
+ pid: null,
5730
+ daemon_version: null,
5731
+ mode: null,
5732
+ private_metadata: false,
5733
+ platform: null,
5734
+ os_version: null,
5735
+ os_build: null,
5736
+ arch: null,
5737
+ uptime_seconds: null,
5738
+ tool_versions: null,
5739
+ tailscale: null,
5740
+ storage_sync_status: null,
5741
+ storage_sync_last_error: null,
5742
+ doctor_summary: null
5743
+ },
5525
5744
  tailscale: { dns_name: null, ips: [], online: null, active: null, last_seen: null },
5526
5745
  ssh: { address: null, route: "local", command_target: "localhost" },
5527
5746
  route_hints: [{ kind: "local", target: "localhost", reachable: true }],
package/dist/db.d.ts CHANGED
@@ -7,13 +7,41 @@ export declare class SqliteAdapter {
7
7
  export declare function getAdapter(path?: string): SqliteAdapter;
8
8
  export declare function getDb(path?: string): Database;
9
9
  export declare function closeDb(): void;
10
- export declare function upsertHeartbeat(machineId: string, pid?: number, status?: "online" | "offline"): void;
10
+ export interface HeartbeatUpsertMetadata {
11
+ daemonVersion?: string | null;
12
+ agentMode?: string | null;
13
+ platform?: string | null;
14
+ osVersion?: string | null;
15
+ osBuild?: string | null;
16
+ arch?: string | null;
17
+ uptimeSeconds?: number | null;
18
+ toolVersions?: Record<string, unknown> | null;
19
+ tailscale?: Record<string, unknown> | null;
20
+ storageSyncStatus?: string | null;
21
+ storageSyncLastError?: string | null;
22
+ doctorSummary?: Record<string, unknown> | null;
23
+ privateMetadata?: boolean;
24
+ }
25
+ export declare function upsertHeartbeat(machineId: string, pid?: number, status?: "online" | "offline", metadata?: HeartbeatUpsertMetadata): void;
11
26
  export declare function getLocalMachineId(): string;
12
27
  export interface StoredHeartbeat {
13
28
  machine_id: string;
14
29
  pid: number;
15
30
  status: string;
16
31
  updated_at: string;
32
+ daemon_version: string | null;
33
+ agent_mode: string | null;
34
+ platform: string | null;
35
+ os_version: string | null;
36
+ os_build: string | null;
37
+ arch: string | null;
38
+ uptime_seconds: number | null;
39
+ tool_versions_json: string | null;
40
+ tailscale_json: string | null;
41
+ storage_sync_status: string | null;
42
+ storage_sync_last_error: string | null;
43
+ doctor_summary_json: string | null;
44
+ private_metadata: number;
17
45
  }
18
46
  export declare function listHeartbeats(machineId?: string): StoredHeartbeat[];
19
47
  export declare function countRuns(table: "setup_runs" | "sync_runs"): number;
package/dist/db.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAItC,qBAAa,aAAa;IACxB,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC;gBAEX,IAAI,EAAE,MAAM;IAIxB,KAAK,IAAI,IAAI;CAGd;AAsCD,wBAAgB,UAAU,CAAC,IAAI,SAAc,GAAG,aAAa,CAqB5D;AAED,wBAAgB,KAAK,CAAC,IAAI,SAAc,GAAG,QAAQ,CAElD;AAED,wBAAgB,OAAO,IAAI,IAAI,CAK9B;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,SAAc,EAAE,MAAM,GAAE,QAAQ,GAAG,SAAoB,GAAG,IAAI,CASnH;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,cAAc,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,eAAe,EAAE,CAoBpE;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,YAAY,GAAG,WAAW,GAAG,MAAM,CAInE;AAED,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,SAAS,GAAG,IAAI,CAOrG;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAOxF;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAOvF"}
1
+ {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -11,6 +11,7 @@ export * from "./commands/backup.js";
11
11
  export * from "./commands/apps.js";
12
12
  export * from "./commands/cert.js";
13
13
  export * from "./commands/dns.js";
14
+ export * from "./commands/daemon.js";
14
15
  export * from "./commands/doctor.js";
15
16
  export * from "./commands/manifest.js";
16
17
  export * from "./commands/diff.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,0BAA0B,CAAC;AACzC,cAAc,YAAY,CAAC;AAC3B,cAAc,SAAS,CAAC;AACxB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AACnC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,iCAAiC,CAAC;AAChD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,iBAAiB,CAAC;AAChC,OAAO,EACL,oBAAoB,EACpB,6BAA6B,EAC7B,yBAAyB,EACzB,kCAAkC,EAClC,uBAAuB,EACvB,oBAAoB,EACpB,gBAAgB,EAChB,cAAc,EACd,qBAAqB,EACrB,yBAAyB,EACzB,qBAAqB,EACrB,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,oBAAoB,EACpB,WAAW,EACX,WAAW,EACX,WAAW,GACZ,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,aAAa,EAAE,QAAQ,EAAE,UAAU,IAAI,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACtH,cAAc,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,0BAA0B,CAAC;AACzC,cAAc,YAAY,CAAC;AAC3B,cAAc,SAAS,CAAC;AACxB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AACnC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,iCAAiC,CAAC;AAChD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,iBAAiB,CAAC;AAChC,OAAO,EACL,oBAAoB,EACpB,6BAA6B,EAC7B,yBAAyB,EACzB,kCAAkC,EAClC,uBAAuB,EACvB,oBAAoB,EACpB,gBAAgB,EAChB,cAAc,EACd,qBAAqB,EACrB,yBAAyB,EACzB,qBAAqB,EACrB,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,oBAAoB,EACpB,WAAW,EACX,WAAW,EACX,WAAW,GACZ,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,aAAa,EAAE,QAAQ,EAAE,UAAU,IAAI,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACtH,cAAc,cAAc,CAAC"}