@hasna/machines 0.0.23 → 0.0.25

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/consumer.js CHANGED
@@ -4264,6 +4264,7 @@ function writeManifest(manifest, path = getManifestPath()) {
4264
4264
  generatedAt: new Date().toISOString(),
4265
4265
  machines: normalizeMachines(manifest.machines)
4266
4266
  };
4267
+ fleetSchema.parse(payload);
4267
4268
  writeFileSync(path, `${JSON.stringify(payload, null, 2)}
4268
4269
  `, "utf8");
4269
4270
  return path;
@@ -4312,29 +4313,78 @@ function getPackageVersion() {
4312
4313
  var MACHINES_CONSUMER_CONTRACT_VERSION = 1;
4313
4314
  var MACHINES_PACKAGE_NAME = "@hasna/machines";
4314
4315
  var MACHINES_CONSUMER_ENTRYPOINT = "@hasna/machines/consumer";
4316
+ var MACHINES_CONSUMER_SCHEMA_URI = "https://schemas.hasna.xyz/machines/consumer/v1/machines-consumer.schema.json";
4317
+ var MACHINES_CONSUMER_SCHEMA_ARTIFACT = "schemas/machines-consumer.schema.json";
4318
+ var DEFAULT_MACHINE_RESOLVER_TTL_MS = 24 * 60 * 60 * 1000;
4315
4319
  var MACHINES_CONSUMER_CAPABILITIES = {
4316
4320
  topology: true,
4317
4321
  compatibility: true,
4318
4322
  route_resolution: true,
4319
4323
  cli_json_fallback: true,
4320
4324
  workspace_path_mapping: true,
4321
- workspace_diagnostics: true
4325
+ workspace_diagnostics: true,
4326
+ schema_artifacts: true,
4327
+ cacheability_metadata: true,
4328
+ resolver_snapshots: true,
4329
+ field_capability_descriptors: true
4330
+ };
4331
+ var MACHINES_CONSUMER_FIELD_CAPABILITIES = {
4332
+ topology: {
4333
+ machine_identity: true,
4334
+ route_hints: true,
4335
+ tailscale_status: true,
4336
+ manifest_metadata: true
4337
+ },
4338
+ route: {
4339
+ cacheability: true,
4340
+ confidence: true,
4341
+ resolver_evidence: true
4342
+ },
4343
+ workspace: {
4344
+ cacheability: true,
4345
+ path_mapping: true,
4346
+ diagnostics: true,
4347
+ repair_hints: true,
4348
+ trust_auth: true
4349
+ },
4350
+ compatibility: {
4351
+ commands: true,
4352
+ packages: true,
4353
+ workspaces: true
4354
+ },
4355
+ resolver_snapshot: {
4356
+ cacheability: true,
4357
+ redacted_provenance: true
4358
+ }
4322
4359
  };
4323
4360
  var MACHINES_CONSUMER_CONTRACT = {
4324
4361
  schema_version: MACHINES_CONSUMER_CONTRACT_VERSION,
4325
4362
  package_name: MACHINES_PACKAGE_NAME,
4326
4363
  entrypoint: MACHINES_CONSUMER_ENTRYPOINT,
4364
+ schema_uri: MACHINES_CONSUMER_SCHEMA_URI,
4365
+ schema_artifact: MACHINES_CONSUMER_SCHEMA_ARTIFACT,
4327
4366
  capabilities: MACHINES_CONSUMER_CAPABILITIES,
4328
- envelopes: ["topology", "route", "workspace", "compatibility"],
4367
+ field_capabilities: MACHINES_CONSUMER_FIELD_CAPABILITIES,
4368
+ cacheability: {
4369
+ default_ttl_ms: DEFAULT_MACHINE_RESOLVER_TTL_MS,
4370
+ stale_requires_refresh: true
4371
+ },
4372
+ envelopes: ["topology", "route", "workspace", "compatibility", "resolver_snapshot"],
4329
4373
  stable_exports: [
4330
4374
  "MACHINES_CONSUMER_CONTRACT",
4331
4375
  "MACHINES_CONSUMER_CONTRACT_VERSION",
4332
4376
  "MACHINES_CONSUMER_CAPABILITIES",
4377
+ "MACHINES_CONSUMER_FIELD_CAPABILITIES",
4378
+ "MACHINES_CONSUMER_SCHEMA_BUNDLE",
4379
+ "MACHINES_CONSUMER_SCHEMA_URI",
4333
4380
  "MACHINES_PACKAGE_NAME",
4334
4381
  "discoverMachineTopology",
4335
4382
  "getLocalMachineTopology",
4336
4383
  "resolveMachineRoute",
4337
4384
  "resolveMachineWorkspace",
4385
+ "createMachineResolverSnapshot",
4386
+ "getMachinesConsumerSchemaBundle",
4387
+ "validateMachinesConsumerEnvelope",
4338
4388
  "checkMachineCompatibility",
4339
4389
  "resolveMachineCommand",
4340
4390
  "runMachineCommand",
@@ -4612,11 +4662,100 @@ function routeConfidence(input) {
4612
4662
  return "low";
4613
4663
  return "none";
4614
4664
  }
4665
+ function addMilliseconds(date, milliseconds) {
4666
+ return new Date(date.getTime() + milliseconds).toISOString();
4667
+ }
4668
+ function routeAuthority(input) {
4669
+ if (!input.machine)
4670
+ return "unresolved";
4671
+ if (input.matchedBy === "fallback")
4672
+ return "fallback";
4673
+ if (input.selectedHint?.kind === "local")
4674
+ return "live_topology";
4675
+ if (input.selectedHint?.kind === "tailscale" || input.machine.tailscale.online !== null)
4676
+ return "live_topology";
4677
+ if (input.machine.manifest_declared)
4678
+ return "manifest";
4679
+ return "open-machines";
4680
+ }
4681
+ function workspaceAuthority(paths) {
4682
+ const sources = [paths.workspace_root.source, paths.project_root.source, paths.open_files_root.source];
4683
+ if (sources.some((source) => source === "argument"))
4684
+ return "argument";
4685
+ if (sources.some((source) => source === "manifest_metadata"))
4686
+ return "manifest_metadata";
4687
+ if (sources.some((source) => source === "manifest"))
4688
+ return "manifest";
4689
+ if (sources.some((source) => source === "inferred"))
4690
+ return "inferred";
4691
+ if (sources.every((source) => source === "unresolved"))
4692
+ return "unresolved";
4693
+ return "open-machines";
4694
+ }
4695
+ function cacheability(input) {
4696
+ const ttlMs = input.ttlMs === undefined ? DEFAULT_MACHINE_RESOLVER_TTL_MS : input.ttlMs;
4697
+ const expiresAt = typeof ttlMs === "number" && ttlMs > 0 ? addMilliseconds(input.observedAt, ttlMs) : null;
4698
+ const stale = expiresAt ? input.now.getTime() > new Date(expiresAt).getTime() : false;
4699
+ const confidenceCacheable = input.confidence !== "none" && input.confidence !== "low";
4700
+ const cacheable = input.ok && confidenceCacheable && !stale && input.authority !== "unresolved";
4701
+ const reasons = [...input.reasons];
4702
+ if (!input.ok)
4703
+ reasons.push("resolver_not_ok");
4704
+ if (!confidenceCacheable)
4705
+ reasons.push(`low_confidence:${input.confidence}`);
4706
+ if (stale)
4707
+ reasons.push("stale");
4708
+ if (input.authority === "unresolved")
4709
+ reasons.push("unresolved_authority");
4710
+ return {
4711
+ observed_at: input.observedAt.toISOString(),
4712
+ verified_at: input.ok ? input.now.toISOString() : null,
4713
+ expires_at: expiresAt,
4714
+ ttl_ms: typeof ttlMs === "number" && ttlMs > 0 ? ttlMs : null,
4715
+ source_authority: input.authority,
4716
+ confidence: input.confidence,
4717
+ cacheable,
4718
+ stale,
4719
+ reasons: [...new Set(reasons)].sort()
4720
+ };
4721
+ }
4722
+ function worstConfidence(values) {
4723
+ const rank = {
4724
+ exact: 0,
4725
+ high: 1,
4726
+ medium: 2,
4727
+ low: 3,
4728
+ none: 4
4729
+ };
4730
+ return [...values].sort((left, right) => rank[right] - rank[left])[0] ?? "none";
4731
+ }
4732
+ function mergeAuthorities(values) {
4733
+ const filtered = values.filter((value) => value !== "unknown");
4734
+ if (filtered.length === 0)
4735
+ return "unknown";
4736
+ const first = filtered[0];
4737
+ return filtered.every((value) => value === first) ? first : "mixed";
4738
+ }
4739
+ function mergeCacheability(input) {
4740
+ const confidence = worstConfidence(input.cacheabilities.map((entry) => entry.confidence));
4741
+ const reasons = input.cacheabilities.flatMap((entry) => entry.reasons);
4742
+ const ok = input.cacheabilities.every((entry) => entry.cacheable);
4743
+ return cacheability({
4744
+ ok,
4745
+ observedAt: input.observedAt,
4746
+ now: input.now,
4747
+ ttlMs: input.ttlMs,
4748
+ authority: mergeAuthorities(input.authorities),
4749
+ confidence,
4750
+ reasons
4751
+ });
4752
+ }
4615
4753
  function resolveMachineRoute(machineId, options = {}) {
4754
+ const now = options.now ?? new Date;
4616
4755
  const topology = options.topology ?? discoverMachineTopology(options);
4617
4756
  const warnings = [...topology.warnings];
4618
4757
  const { machine, matchedBy } = findRouteMachine(topology, machineId);
4619
- const generatedAt = (options.now ?? new Date).toISOString();
4758
+ const generatedAt = now.toISOString();
4620
4759
  if (!machine) {
4621
4760
  warnings.push(`machine_not_found:${machineId}`);
4622
4761
  return {
@@ -4640,16 +4779,27 @@ function resolveMachineRoute(machineId, options = {}) {
4640
4779
  tailscale_online: null,
4641
4780
  selected_hint: null
4642
4781
  },
4782
+ cacheability: cacheability({
4783
+ ok: false,
4784
+ observedAt: now,
4785
+ now,
4786
+ ttlMs: options.resolverTtlMs,
4787
+ authority: "unresolved",
4788
+ confidence: "none",
4789
+ reasons: [`machine_not_found:${machineId}`]
4790
+ }),
4643
4791
  warnings
4644
4792
  };
4645
4793
  }
4646
4794
  const selectedHint = selectRouteHint(machine.route_hints);
4647
4795
  const route = selectedHint?.kind ?? machine.ssh.route ?? "unknown";
4648
4796
  const local = route === "local" || machine.machine_id === topology.local_machine_id;
4797
+ const confidence = routeConfidence({ machine, hint: selectedHint, matchedBy });
4798
+ const ok = Boolean(selectedHint?.target);
4649
4799
  return {
4650
4800
  schema_version: MACHINES_CONSUMER_CONTRACT_VERSION,
4651
4801
  package: topology.package,
4652
- ok: Boolean(selectedHint?.target),
4802
+ ok,
4653
4803
  machine_id: machine.machine_id,
4654
4804
  requested_machine_id: machineId,
4655
4805
  generated_at: generatedAt,
@@ -4657,7 +4807,7 @@ function resolveMachineRoute(machineId, options = {}) {
4657
4807
  source: route,
4658
4808
  target: selectedHint?.target ?? null,
4659
4809
  command_target: selectedHint?.target ?? null,
4660
- confidence: routeConfidence({ machine, hint: selectedHint, matchedBy }),
4810
+ confidence,
4661
4811
  local,
4662
4812
  evidence: {
4663
4813
  topology: true,
@@ -4667,6 +4817,15 @@ function resolveMachineRoute(machineId, options = {}) {
4667
4817
  tailscale_online: machine.tailscale.online,
4668
4818
  selected_hint: selectedHint
4669
4819
  },
4820
+ cacheability: cacheability({
4821
+ ok,
4822
+ observedAt: now,
4823
+ now,
4824
+ ttlMs: options.resolverTtlMs,
4825
+ authority: routeAuthority({ machine, selectedHint, matchedBy }),
4826
+ confidence,
4827
+ reasons: selectedHint ? [] : ["route_target_unresolved"]
4828
+ }),
4670
4829
  warnings
4671
4830
  };
4672
4831
  }
@@ -4963,10 +5122,11 @@ function metadataKeysForDiagnostics(metadata) {
4963
5122
  return Object.keys(metadata).filter((key) => !/(secret|token|key|password|credential)/i.test(key)).sort();
4964
5123
  }
4965
5124
  function resolveMachineWorkspace(options) {
5125
+ const now = options.now ?? new Date;
4966
5126
  const topology = options.topology ?? discoverMachineTopology(options);
4967
5127
  const warnings = [...topology.warnings];
4968
5128
  const { machine, matchedBy } = findRouteMachine(topology, options.machineId);
4969
- const generatedAt = (options.now ?? new Date).toISOString();
5129
+ const generatedAt = now.toISOString();
4970
5130
  const repoName = options.repoName ?? options.projectId;
4971
5131
  const openFilesRepoName = options.openFilesRepoName ?? "open-files";
4972
5132
  if (!machine) {
@@ -4993,6 +5153,15 @@ function resolveMachineWorkspace(options) {
4993
5153
  manifest_declared: null,
4994
5154
  metadata_keys: []
4995
5155
  },
5156
+ cacheability: cacheability({
5157
+ ok: false,
5158
+ observedAt: now,
5159
+ now,
5160
+ ttlMs: options.resolverTtlMs,
5161
+ authority: "unresolved",
5162
+ confidence: "none",
5163
+ reasons: [`machine_not_found:${options.machineId}`]
5164
+ }),
4996
5165
  warnings
4997
5166
  };
4998
5167
  const diagnostics2 = workspaceDiagnostics({
@@ -5024,10 +5193,16 @@ function resolveMachineWorkspace(options) {
5024
5193
  warnings.push(`open_files_root_inferred:${options.projectId}`);
5025
5194
  if (!projectRootPath)
5026
5195
  warnings.push(`project_root_unresolved:${options.projectId}`);
5196
+ const workspacePaths = {
5197
+ workspace_root: { path: workspaceRootPath, source: workspaceRootSource },
5198
+ project_root: { path: projectRootPath, source: projectRootSource },
5199
+ open_files_root: { path: openFilesRootPath, source: openFilesRootSource }
5200
+ };
5201
+ const workspaceOk = Boolean(projectRootPath);
5027
5202
  const resolution = {
5028
5203
  schema_version: MACHINES_CONSUMER_CONTRACT_VERSION,
5029
5204
  package: topology.package,
5030
- ok: Boolean(projectRootPath),
5205
+ ok: workspaceOk,
5031
5206
  requested_machine_id: options.machineId,
5032
5207
  machine_id: machine.machine_id,
5033
5208
  generated_at: generatedAt,
@@ -5042,11 +5217,7 @@ function resolveMachineWorkspace(options) {
5042
5217
  trust_status: trustStatus(machine),
5043
5218
  auth_status: authStatus(machine)
5044
5219
  },
5045
- paths: {
5046
- workspace_root: { path: workspaceRootPath, source: workspaceRootSource },
5047
- project_root: { path: projectRootPath, source: projectRootSource },
5048
- open_files_root: { path: openFilesRootPath, source: openFilesRootSource }
5049
- },
5220
+ paths: workspacePaths,
5050
5221
  diagnostics: [],
5051
5222
  repair_hints: [],
5052
5223
  evidence: {
@@ -5055,6 +5226,15 @@ function resolveMachineWorkspace(options) {
5055
5226
  manifest_declared: machine.manifest_declared,
5056
5227
  metadata_keys: metadataKeysForDiagnostics(metadata)
5057
5228
  },
5229
+ cacheability: cacheability({
5230
+ ok: workspaceOk,
5231
+ observedAt: now,
5232
+ now,
5233
+ ttlMs: options.resolverTtlMs,
5234
+ authority: workspaceAuthority(workspacePaths),
5235
+ confidence: workspaceOk ? "medium" : "none",
5236
+ reasons: projectRootPath ? [] : [`project_root_unresolved:${options.projectId}`]
5237
+ }),
5058
5238
  warnings
5059
5239
  };
5060
5240
  const diagnostics = workspaceDiagnostics({
@@ -5069,6 +5249,73 @@ function resolveMachineWorkspace(options) {
5069
5249
  repair_hints: diagnostics.repairHints
5070
5250
  };
5071
5251
  }
5252
+ function createMachineResolverSnapshot(options) {
5253
+ const now = options.now ?? new Date;
5254
+ const routeObservedAt = new Date(options.route.generated_at);
5255
+ const workspaceObservedAt = options.workspace ? new Date(options.workspace.generated_at) : null;
5256
+ const observedAt = new Date(Math.max(Number.isNaN(routeObservedAt.getTime()) ? 0 : routeObservedAt.getTime(), workspaceObservedAt && !Number.isNaN(workspaceObservedAt.getTime()) ? workspaceObservedAt.getTime() : 0));
5257
+ const cacheabilities = [options.route.cacheability, options.workspace?.cacheability].filter((entry) => Boolean(entry));
5258
+ const authorities = cacheabilities.map((entry) => entry.source_authority);
5259
+ const warnings = [...new Set([
5260
+ ...options.route.warnings,
5261
+ ...options.workspace?.warnings ?? [],
5262
+ ...cacheabilities.flatMap((entry) => entry.reasons)
5263
+ ])].sort();
5264
+ return {
5265
+ schema_version: MACHINES_CONSUMER_CONTRACT_VERSION,
5266
+ package: options.route.package,
5267
+ generated_at: now.toISOString(),
5268
+ requested_machine_id: options.route.requested_machine_id,
5269
+ machine_id: options.route.machine_id ?? options.workspace?.machine_id ?? null,
5270
+ route: {
5271
+ ok: options.route.ok,
5272
+ source: options.route.source,
5273
+ route: options.route.route,
5274
+ target: options.route.target,
5275
+ command_target: options.route.command_target,
5276
+ confidence: options.route.confidence,
5277
+ local: options.route.local,
5278
+ cacheability: options.route.cacheability
5279
+ },
5280
+ workspace: options.workspace ? {
5281
+ ok: options.workspace.ok,
5282
+ project: options.workspace.project,
5283
+ machine: options.workspace.machine,
5284
+ paths: options.workspace.paths,
5285
+ diagnostics: options.workspace.diagnostics,
5286
+ repair_hints: options.workspace.repair_hints,
5287
+ cacheability: options.workspace.cacheability
5288
+ } : null,
5289
+ cacheability: mergeCacheability({
5290
+ observedAt: observedAt.getTime() > 0 ? observedAt : now,
5291
+ now,
5292
+ ttlMs: options.resolverTtlMs,
5293
+ authorities,
5294
+ cacheabilities
5295
+ }),
5296
+ warnings,
5297
+ provenance: {
5298
+ route: {
5299
+ schema_version: options.route.schema_version,
5300
+ generated_at: options.route.generated_at,
5301
+ evidence: {
5302
+ matched_by: options.route.evidence.matched_by,
5303
+ manifest_declared: options.route.evidence.manifest_declared,
5304
+ heartbeat_status: options.route.evidence.heartbeat_status,
5305
+ tailscale_online: options.route.evidence.tailscale_online,
5306
+ selected_hint_kind: options.route.evidence.selected_hint?.kind ?? null
5307
+ }
5308
+ },
5309
+ workspace: options.workspace ? {
5310
+ schema_version: options.workspace.schema_version,
5311
+ generated_at: options.workspace.generated_at,
5312
+ metadata_keys: options.workspace.evidence.metadata_keys,
5313
+ matched_by: options.workspace.evidence.matched_by,
5314
+ manifest_declared: options.workspace.evidence.manifest_declared
5315
+ } : null
5316
+ }
5317
+ };
5318
+ }
5072
5319
  function getLocalMachineTopology(options = {}) {
5073
5320
  const topology = discoverMachineTopology(options);
5074
5321
  return topology.machines.find((machine) => machine.machine_id === topology.local_machine_id) ?? {
@@ -5088,6 +5335,237 @@ function getLocalMachineTopology(options = {}) {
5088
5335
  metadata: {}
5089
5336
  };
5090
5337
  }
5338
+ // src/consumer-schema.ts
5339
+ var MACHINES_CONSUMER_SCHEMA_BUNDLE = {
5340
+ $schema: "https://json-schema.org/draft/2020-12/schema",
5341
+ $id: MACHINES_CONSUMER_SCHEMA_URI,
5342
+ title: "@hasna/machines consumer contract schema bundle",
5343
+ type: "object",
5344
+ $defs: {
5345
+ cacheability: {
5346
+ type: "object",
5347
+ required: ["observed_at", "verified_at", "expires_at", "ttl_ms", "source_authority", "confidence", "cacheable", "stale", "reasons"],
5348
+ properties: {
5349
+ observed_at: { type: "string", format: "date-time" },
5350
+ verified_at: { type: ["string", "null"], format: "date-time" },
5351
+ expires_at: { type: ["string", "null"], format: "date-time" },
5352
+ ttl_ms: { type: ["number", "null"] },
5353
+ source_authority: { enum: ["open-machines", "manifest", "manifest_metadata", "live_topology", "argument", "inferred", "fallback", "unresolved", "mixed", "unknown"] },
5354
+ confidence: { enum: ["exact", "high", "medium", "low", "none"] },
5355
+ cacheable: { type: "boolean" },
5356
+ stale: { type: "boolean" },
5357
+ reasons: { type: "array", items: { type: "string" } }
5358
+ }
5359
+ },
5360
+ contract: {
5361
+ type: "object",
5362
+ required: ["schema_version", "package_name", "entrypoint", "schema_uri", "schema_artifact", "capabilities", "field_capabilities", "cacheability", "envelopes", "stable_exports"],
5363
+ properties: {
5364
+ schema_version: { const: MACHINES_CONSUMER_CONTRACT_VERSION },
5365
+ package_name: { const: "@hasna/machines" },
5366
+ entrypoint: { const: "@hasna/machines/consumer" },
5367
+ schema_uri: { const: MACHINES_CONSUMER_SCHEMA_URI },
5368
+ schema_artifact: { const: "schemas/machines-consumer.schema.json" },
5369
+ capabilities: { type: "object" },
5370
+ field_capabilities: { type: "object" },
5371
+ cacheability: { type: "object" },
5372
+ envelopes: { type: "array", items: { enum: ["topology", "route", "workspace", "compatibility", "resolver_snapshot"] } },
5373
+ stable_exports: { type: "array", items: { type: "string" } }
5374
+ }
5375
+ },
5376
+ topology: {
5377
+ type: "object",
5378
+ required: ["schema_version", "package", "capabilities", "generated_at", "local_machine_id", "machines", "warnings"],
5379
+ properties: {
5380
+ schema_version: { const: MACHINES_CONSUMER_CONTRACT_VERSION },
5381
+ package: { type: "object" },
5382
+ capabilities: { type: "object" },
5383
+ generated_at: { type: "string", format: "date-time" },
5384
+ local_machine_id: { type: "string" },
5385
+ machines: { type: "array" },
5386
+ warnings: { type: "array", items: { type: "string" } }
5387
+ }
5388
+ },
5389
+ route: {
5390
+ type: "object",
5391
+ required: ["schema_version", "package", "ok", "machine_id", "requested_machine_id", "generated_at", "route", "source", "target", "command_target", "confidence", "local", "evidence", "cacheability", "warnings"],
5392
+ properties: {
5393
+ schema_version: { const: MACHINES_CONSUMER_CONTRACT_VERSION },
5394
+ package: { type: "object" },
5395
+ ok: { type: "boolean" },
5396
+ machine_id: { type: ["string", "null"] },
5397
+ requested_machine_id: { type: "string" },
5398
+ generated_at: { type: "string", format: "date-time" },
5399
+ route: { enum: ["local", "lan", "tailscale", "ssh", "unknown"] },
5400
+ source: { enum: ["local", "lan", "tailscale", "ssh", "unknown"] },
5401
+ target: { type: ["string", "null"] },
5402
+ command_target: { type: ["string", "null"] },
5403
+ confidence: { enum: ["exact", "high", "medium", "low", "none"] },
5404
+ local: { type: "boolean" },
5405
+ evidence: { type: "object" },
5406
+ cacheability: { $ref: "#/$defs/cacheability" },
5407
+ warnings: { type: "array", items: { type: "string" } }
5408
+ }
5409
+ },
5410
+ workspace: {
5411
+ type: "object",
5412
+ required: ["schema_version", "package", "ok", "requested_machine_id", "machine_id", "generated_at", "project", "machine", "paths", "diagnostics", "repair_hints", "evidence", "cacheability", "warnings"],
5413
+ properties: {
5414
+ schema_version: { const: MACHINES_CONSUMER_CONTRACT_VERSION },
5415
+ package: { type: "object" },
5416
+ ok: { type: "boolean" },
5417
+ requested_machine_id: { type: "string" },
5418
+ machine_id: { type: ["string", "null"] },
5419
+ generated_at: { type: "string", format: "date-time" },
5420
+ project: { type: "object" },
5421
+ machine: {
5422
+ type: "object",
5423
+ required: ["current", "primary", "trust_status", "auth_status"],
5424
+ properties: {
5425
+ current: { type: "boolean" },
5426
+ primary: { type: "boolean" },
5427
+ trust_status: { enum: ["trusted", "untrusted", "unknown"] },
5428
+ auth_status: { enum: ["authenticated", "unauthenticated", "unknown"] }
5429
+ }
5430
+ },
5431
+ paths: { type: "object" },
5432
+ diagnostics: { type: "array" },
5433
+ repair_hints: { type: "array" },
5434
+ evidence: { type: "object" },
5435
+ cacheability: { $ref: "#/$defs/cacheability" },
5436
+ warnings: { type: "array", items: { type: "string" } }
5437
+ }
5438
+ },
5439
+ compatibility: {
5440
+ type: "object",
5441
+ required: ["schema_version", "package", "capabilities", "ok", "machine_id", "source", "generated_at", "checks", "summary"],
5442
+ properties: {
5443
+ schema_version: { const: MACHINES_CONSUMER_CONTRACT_VERSION },
5444
+ package: { type: "object" },
5445
+ capabilities: { type: "object" },
5446
+ ok: { type: "boolean" },
5447
+ machine_id: { type: "string" },
5448
+ source: { type: "string" },
5449
+ generated_at: { type: "string", format: "date-time" },
5450
+ checks: { type: "array" },
5451
+ summary: { type: "object" }
5452
+ }
5453
+ },
5454
+ resolver_snapshot: {
5455
+ type: "object",
5456
+ required: ["schema_version", "package", "generated_at", "requested_machine_id", "machine_id", "route", "workspace", "cacheability", "warnings", "provenance"],
5457
+ properties: {
5458
+ schema_version: { const: MACHINES_CONSUMER_CONTRACT_VERSION },
5459
+ package: { type: "object" },
5460
+ generated_at: { type: "string", format: "date-time" },
5461
+ requested_machine_id: { type: "string" },
5462
+ machine_id: { type: ["string", "null"] },
5463
+ route: { type: "object" },
5464
+ workspace: { type: ["object", "null"] },
5465
+ cacheability: { $ref: "#/$defs/cacheability" },
5466
+ warnings: { type: "array", items: { type: "string" } },
5467
+ provenance: { type: "object" }
5468
+ }
5469
+ }
5470
+ }
5471
+ };
5472
+ function isRecord2(value) {
5473
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
5474
+ }
5475
+ function hasString(value, key) {
5476
+ return typeof value[key] === "string" && value[key].length > 0;
5477
+ }
5478
+ function hasObject(value, key) {
5479
+ return isRecord2(value[key]);
5480
+ }
5481
+ function hasArray(value, key) {
5482
+ return Array.isArray(value[key]);
5483
+ }
5484
+ function hasCacheability(value, key = "cacheability") {
5485
+ const cache = value[key];
5486
+ return isRecord2(cache) && hasString(cache, "observed_at") && typeof cache.cacheable === "boolean" && typeof cache.stale === "boolean" && hasArray(cache, "reasons");
5487
+ }
5488
+ function requireFields(value, fields, errors2) {
5489
+ for (const field of fields) {
5490
+ if (!(field in value))
5491
+ errors2.push(`missing:${field}`);
5492
+ }
5493
+ }
5494
+ function getMachinesConsumerSchemaBundle() {
5495
+ return JSON.parse(JSON.stringify(MACHINES_CONSUMER_SCHEMA_BUNDLE));
5496
+ }
5497
+ function validateMachinesConsumerEnvelope(envelope, value) {
5498
+ const errors2 = [];
5499
+ if (!isRecord2(value)) {
5500
+ return { ok: false, envelope, schema_id: MACHINES_CONSUMER_SCHEMA_URI, errors: ["not_object"] };
5501
+ }
5502
+ if (value.schema_version !== MACHINES_CONSUMER_CONTRACT_VERSION)
5503
+ errors2.push(`schema_version:${String(value.schema_version)}`);
5504
+ if (envelope === "contract") {
5505
+ requireFields(value, ["package_name", "entrypoint", "schema_uri", "schema_artifact", "capabilities", "field_capabilities", "cacheability", "envelopes", "stable_exports"], errors2);
5506
+ if (value.package_name !== "@hasna/machines")
5507
+ errors2.push("package_name");
5508
+ if (value.entrypoint !== "@hasna/machines/consumer")
5509
+ errors2.push("entrypoint");
5510
+ if (!hasObject(value, "capabilities"))
5511
+ errors2.push("capabilities");
5512
+ if (!hasObject(value, "field_capabilities"))
5513
+ errors2.push("field_capabilities");
5514
+ if (!hasArray(value, "envelopes"))
5515
+ errors2.push("envelopes");
5516
+ if (!hasArray(value, "stable_exports"))
5517
+ errors2.push("stable_exports");
5518
+ } else if (envelope === "topology") {
5519
+ requireFields(value, ["package", "capabilities", "generated_at", "local_machine_id", "machines", "warnings"], errors2);
5520
+ if (!hasArray(value, "machines"))
5521
+ errors2.push("machines");
5522
+ if (!hasArray(value, "warnings"))
5523
+ errors2.push("warnings");
5524
+ } else if (envelope === "route") {
5525
+ requireFields(value, ["package", "ok", "machine_id", "requested_machine_id", "generated_at", "route", "source", "target", "command_target", "confidence", "local", "evidence", "cacheability", "warnings"], errors2);
5526
+ if (typeof value.ok !== "boolean")
5527
+ errors2.push("ok");
5528
+ if (!hasObject(value, "evidence"))
5529
+ errors2.push("evidence");
5530
+ if (!hasCacheability(value))
5531
+ errors2.push("cacheability");
5532
+ if (!hasArray(value, "warnings"))
5533
+ errors2.push("warnings");
5534
+ } else if (envelope === "workspace") {
5535
+ requireFields(value, ["package", "ok", "requested_machine_id", "machine_id", "generated_at", "project", "machine", "paths", "diagnostics", "repair_hints", "evidence", "cacheability", "warnings"], errors2);
5536
+ if (typeof value.ok !== "boolean")
5537
+ errors2.push("ok");
5538
+ if (!hasObject(value, "machine"))
5539
+ errors2.push("machine");
5540
+ if (!hasObject(value, "paths"))
5541
+ errors2.push("paths");
5542
+ if (!hasArray(value, "diagnostics"))
5543
+ errors2.push("diagnostics");
5544
+ if (!hasArray(value, "repair_hints"))
5545
+ errors2.push("repair_hints");
5546
+ if (!hasCacheability(value))
5547
+ errors2.push("cacheability");
5548
+ } else if (envelope === "compatibility") {
5549
+ requireFields(value, ["package", "capabilities", "ok", "machine_id", "source", "generated_at", "checks", "summary"], errors2);
5550
+ if (typeof value.ok !== "boolean")
5551
+ errors2.push("ok");
5552
+ if (!hasArray(value, "checks"))
5553
+ errors2.push("checks");
5554
+ if (!hasObject(value, "summary"))
5555
+ errors2.push("summary");
5556
+ } else if (envelope === "resolver_snapshot") {
5557
+ requireFields(value, ["package", "generated_at", "requested_machine_id", "machine_id", "route", "workspace", "cacheability", "warnings", "provenance"], errors2);
5558
+ if (!hasObject(value, "route"))
5559
+ errors2.push("route");
5560
+ if (!hasCacheability(value))
5561
+ errors2.push("cacheability");
5562
+ if (!hasArray(value, "warnings"))
5563
+ errors2.push("warnings");
5564
+ if (!hasObject(value, "provenance"))
5565
+ errors2.push("provenance");
5566
+ }
5567
+ return { ok: errors2.length === 0, envelope, schema_id: MACHINES_CONSUMER_SCHEMA_URI, errors: errors2 };
5568
+ }
5091
5569
  // src/remote.ts
5092
5570
  import { spawnSync as spawnSync2 } from "child_process";
5093
5571
  import { hostname as hostname4 } from "os";
@@ -5397,20 +5875,28 @@ function checkMachineCompatibility(options = {}) {
5397
5875
  };
5398
5876
  }
5399
5877
  export {
5878
+ validateMachinesConsumerEnvelope,
5400
5879
  runMachineCommand,
5401
5880
  resolveSshTarget,
5402
5881
  resolveMachineWorkspace,
5403
5882
  resolveMachineRoute,
5404
5883
  resolveMachineCommand,
5405
5884
  getPackageVersion,
5885
+ getMachinesConsumerSchemaBundle,
5406
5886
  getMachinesConsumerCapabilities,
5407
5887
  getLocalMachineTopology,
5408
5888
  discoverMachineTopology,
5889
+ createMachineResolverSnapshot,
5409
5890
  checkMachineCompatibility,
5410
5891
  buildSshCommand,
5411
5892
  MACHINES_PACKAGE_NAME,
5893
+ MACHINES_CONSUMER_SCHEMA_URI,
5894
+ MACHINES_CONSUMER_SCHEMA_BUNDLE,
5895
+ MACHINES_CONSUMER_SCHEMA_ARTIFACT,
5896
+ MACHINES_CONSUMER_FIELD_CAPABILITIES,
5412
5897
  MACHINES_CONSUMER_ENTRYPOINT,
5413
5898
  MACHINES_CONSUMER_CONTRACT_VERSION,
5414
5899
  MACHINES_CONSUMER_CONTRACT,
5415
- MACHINES_CONSUMER_CAPABILITIES
5900
+ MACHINES_CONSUMER_CAPABILITIES,
5901
+ DEFAULT_MACHINE_RESOLVER_TTL_MS
5416
5902
  };
package/dist/index.d.ts CHANGED
@@ -21,6 +21,7 @@ export * from "./commands/self-test.js";
21
21
  export * from "./commands/serve.js";
22
22
  export * from "./commands/setup.js";
23
23
  export * from "./commands/ssh.js";
24
+ export * from "./commands/screen.js";
24
25
  export * from "./commands/sync.js";
25
26
  export * from "./commands/status.js";
26
27
  export * from "./commands/workspace.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,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,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,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,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,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"}