@farmslot/protocol 0.82.2 → 0.83.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.
Files changed (53) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/contracts/config.d.ts +12 -0
  3. package/dist/contracts/config.d.ts.map +1 -1
  4. package/dist/contracts/config.js +12 -0
  5. package/dist/contracts/config.js.map +1 -1
  6. package/dist/contracts/dispatch.d.ts +2 -0
  7. package/dist/contracts/dispatch.d.ts.map +1 -1
  8. package/dist/contracts/human-publication.d.ts +21 -1
  9. package/dist/contracts/human-publication.d.ts.map +1 -1
  10. package/dist/contracts/human-publication.js +7 -0
  11. package/dist/contracts/human-publication.js.map +1 -1
  12. package/dist/contracts/runs.d.ts +23 -0
  13. package/dist/contracts/runs.d.ts.map +1 -1
  14. package/dist/contracts/runs.js +17 -0
  15. package/dist/contracts/runs.js.map +1 -1
  16. package/dist/contracts/task.d.ts +2 -1
  17. package/dist/contracts/task.d.ts.map +1 -1
  18. package/dist/contracts/task.js +2 -2
  19. package/dist/contracts/task.js.map +1 -1
  20. package/dist/contracts/worker-terminal.d.ts +4 -0
  21. package/dist/contracts/worker-terminal.d.ts.map +1 -1
  22. package/dist/contracts/worker-terminal.js.map +1 -1
  23. package/dist/node/farm-layout.d.ts.map +1 -1
  24. package/dist/node/farm-layout.js +2 -3
  25. package/dist/node/farm-layout.js.map +1 -1
  26. package/dist/node/farm-paths-storage.d.ts +6 -0
  27. package/dist/node/farm-paths-storage.d.ts.map +1 -0
  28. package/dist/node/farm-paths-storage.js +54 -0
  29. package/dist/node/farm-paths-storage.js.map +1 -0
  30. package/dist/node/farm-paths.d.ts +149 -0
  31. package/dist/node/farm-paths.d.ts.map +1 -0
  32. package/dist/node/farm-paths.js +429 -0
  33. package/dist/node/farm-paths.js.map +1 -0
  34. package/dist/node/farmslot-home.d.ts +1 -7
  35. package/dist/node/farmslot-home.d.ts.map +1 -1
  36. package/dist/node/farmslot-home.js +6 -16
  37. package/dist/node/farmslot-home.js.map +1 -1
  38. package/dist/node/slot-by-repo.d.ts.map +1 -1
  39. package/dist/node/slot-by-repo.js +13 -5
  40. package/dist/node/slot-by-repo.js.map +1 -1
  41. package/dist/node/tree-fingerprint.d.ts +9 -0
  42. package/dist/node/tree-fingerprint.d.ts.map +1 -0
  43. package/dist/node/tree-fingerprint.js +30 -0
  44. package/dist/node/tree-fingerprint.js.map +1 -0
  45. package/dist/rpc/nodes.d.ts +2 -0
  46. package/dist/rpc/nodes.d.ts.map +1 -1
  47. package/dist/rpc/nodes.js.map +1 -1
  48. package/dist/rpc/run.d.ts +11 -1
  49. package/dist/rpc/run.d.ts.map +1 -1
  50. package/dist/rpc/run.js.map +1 -1
  51. package/dist/version.d.ts +1 -1
  52. package/dist/version.js +1 -1
  53. package/package.json +11 -1
@@ -0,0 +1,54 @@
1
+ import { readFileSync, realpathSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { expandHome } from './farmslot-home.js';
4
+ function readOptional(file) {
5
+ try {
6
+ return JSON.parse(readFileSync(file, 'utf8'));
7
+ }
8
+ catch (error) {
9
+ if (error instanceof Error && 'code' in error && error.code === 'ENOENT')
10
+ return undefined;
11
+ throw error;
12
+ }
13
+ }
14
+ function record(value) {
15
+ return !!value && typeof value === 'object' && !Array.isArray(value);
16
+ }
17
+ /** Read the same configuration and durable pairing as run-bundle/retained.ts:294.
18
+ * Reporting never creates a store or recovers a migration.
19
+ */
20
+ export function readLegacyStorageFacts(paths) {
21
+ const document = readOptional(path.join(path.dirname(paths.data.runBundles), 'run-bundle-retention.json'));
22
+ let configured;
23
+ if (document !== undefined) {
24
+ if (!record(document) || !record(document.runBundleRetention))
25
+ throw new Error('Invalid runBundleRetention settings.');
26
+ const root = document.runBundleRetention.storeRoot;
27
+ if (root !== undefined) {
28
+ if (typeof root !== 'string')
29
+ throw new Error('Invalid runBundleRetention.storeRoot.');
30
+ configured = expandHome(root);
31
+ if (!path.isAbsolute(configured))
32
+ throw new Error('runBundleRetention.storeRoot must be absolute.');
33
+ configured = path.resolve(configured);
34
+ }
35
+ }
36
+ const binding = readOptional(path.join(paths.state.runs, 'bundle-store.json'));
37
+ if (binding === undefined)
38
+ return { runBundles: configured ?? paths.data.runBundles };
39
+ if (!record(binding) ||
40
+ binding.version !== 1 ||
41
+ typeof binding.id !== 'string' ||
42
+ !binding.id ||
43
+ binding.runsDir !== realpathSync(paths.state.runs) ||
44
+ typeof binding.root !== 'string' ||
45
+ !path.isAbsolute(binding.root))
46
+ throw new Error('Run bundle store pairing does not match the canonical Run store.');
47
+ if (configured && realpathSync(configured) !== realpathSync(binding.root))
48
+ throw new Error('Run bundle store pairing does not match the configured root; use the migration command.');
49
+ const owner = readOptional(path.join(binding.root, 'store.json'));
50
+ if (!owner || JSON.stringify(owner) !== JSON.stringify(binding))
51
+ throw new Error('Run bundle root belongs to a different Run store.');
52
+ return { runBundles: binding.root };
53
+ }
54
+ //# sourceMappingURL=farm-paths-storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"farm-paths-storage.js","sourceRoot":"","sources":["../../src/node/farm-paths-storage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACrD,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEhD,SAAS,YAAY,CAAC,IAAY;IAChC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;IAChD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAC;QAC3F,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,MAAM,CAAC,KAAc;IAC5B,OAAO,CAAC,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AACvE,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,sBAAsB,CAAC,KAAgB;IACrD,MAAM,QAAQ,GAAG,YAAY,CAC3B,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,2BAA2B,CAAC,CAC5E,CAAC;IACF,IAAI,UAA8B,CAAC;IACnC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAC;YAC3D,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;QAC1D,MAAM,IAAI,GAAG,QAAQ,CAAC,kBAAkB,CAAC,SAAS,CAAC;QACnD,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,IAAI,OAAO,IAAI,KAAK,QAAQ;gBAAE,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;YACvF,UAAU,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;YAC9B,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;gBAC9B,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;YACpE,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IACD,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,mBAAmB,CAAC,CAAC,CAAC;IAC/E,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,EAAE,UAAU,EAAE,UAAU,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;IACtF,IACE,CAAC,MAAM,CAAC,OAAO,CAAC;QAChB,OAAO,CAAC,OAAO,KAAK,CAAC;QACrB,OAAO,OAAO,CAAC,EAAE,KAAK,QAAQ;QAC9B,CAAC,OAAO,CAAC,EAAE;QACX,OAAO,CAAC,OAAO,KAAK,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC;QAClD,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ;QAChC,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC;QAE9B,MAAM,IAAI,KAAK,CAAC,kEAAkE,CAAC,CAAC;IACtF,IAAI,UAAU,IAAI,YAAY,CAAC,UAAU,CAAC,KAAK,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC;QACvE,MAAM,IAAI,KAAK,CACb,yFAAyF,CAC1F,CAAC;IACJ,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC;IAClE,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;QAC7D,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACvE,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC;AACtC,CAAC"}
@@ -0,0 +1,149 @@
1
+ export declare const FARM_INSTANCE_CLAIM = ".farmslot-instance";
2
+ export type FarmRole = 'gateway' | 'node';
3
+ export type FarmPathEnv = Readonly<Record<string, string | undefined>>;
4
+ export interface LegacyFarmStorageFacts {
5
+ runBundles: string;
6
+ }
7
+ export interface ResolveFarmPathsOptions {
8
+ home?: string;
9
+ role: FarmRole;
10
+ env?: FarmPathEnv;
11
+ checkoutRoot?: string;
12
+ agentRuntimeScripts?: string;
13
+ userHome?: string;
14
+ legacyFacts?: LegacyFarmStorageFacts;
15
+ /** A marker read on the target by deployment tooling; null means absent. */
16
+ instanceConfig?: InstanceConfig | null;
17
+ }
18
+ export interface FarmStatePaths {
19
+ runs: string;
20
+ taskPins: string;
21
+ nativeSessions: string;
22
+ reviewWorkspaces: string;
23
+ sources: string;
24
+ nodeLink: string;
25
+ logs: string;
26
+ tmp: string;
27
+ cache: string;
28
+ gatewayLock: string;
29
+ sessionArchives: string;
30
+ episodes: string;
31
+ reviewWorkspaceSupport: string;
32
+ browserCache: string;
33
+ copilotRuntime: string;
34
+ assessmentArtifacts: string;
35
+ pressureHistory: string;
36
+ intelligenceAudit: string;
37
+ feedbackLedger: string;
38
+ extraLogDirs: string[];
39
+ }
40
+ export interface GatewayFarmStatePaths extends FarmStatePaths {
41
+ /** Legacy uses the backlog JSON file; layout 2 uses the backlog directory. */
42
+ backlog: string;
43
+ backlogFile: string;
44
+ roadmap: string;
45
+ workGraphs: string;
46
+ copilot: string;
47
+ assessments: string;
48
+ analytics: string;
49
+ backlogSpecs: string;
50
+ refinementPrompts: string;
51
+ dispatchQueue: string;
52
+ statusFile: string;
53
+ evalCaps: string;
54
+ observabilityAgreement: string;
55
+ backlogRefinementPrompts: string;
56
+ resourceWatchState: string;
57
+ capabilityStore: string;
58
+ }
59
+ export interface FarmCodePaths {
60
+ checkout: string;
61
+ agentRuntimeScripts: string;
62
+ templateNode: string;
63
+ node: string;
64
+ cli: string;
65
+ bin: string;
66
+ support: string;
67
+ }
68
+ export interface FarmDataPaths {
69
+ /** Legacy stores have no common data root; this is the selected farm home. */
70
+ root: string;
71
+ /** Legacy default candidate unless legacyFacts is supplied. Reports use resolveReportedFarmPaths. */
72
+ runBundles: string;
73
+ runArchive: string;
74
+ taskPinsArchive: string;
75
+ canary: string;
76
+ datasets: string;
77
+ }
78
+ interface SharedFarmPaths {
79
+ layout: 'legacy' | 2;
80
+ instance: string;
81
+ home: string;
82
+ /** Per-store environment variable names actually honoured, for migration warnings. */
83
+ envOverrides: string[];
84
+ config: string;
85
+ configFiles: {
86
+ sources: string;
87
+ };
88
+ pool: string;
89
+ projects: string;
90
+ templateProjects: string;
91
+ code: FarmCodePaths;
92
+ backups: string;
93
+ data: FarmDataPaths;
94
+ }
95
+ export interface NodeFarmPaths extends SharedFarmPaths {
96
+ role: 'node';
97
+ state: FarmStatePaths;
98
+ }
99
+ export interface GatewayFarmPaths extends SharedFarmPaths {
100
+ role: 'gateway';
101
+ state: GatewayFarmStatePaths;
102
+ }
103
+ export type FarmPaths = NodeFarmPaths | GatewayFarmPaths;
104
+ /** Addressable path leaves, including indexed path arrays. Metadata is never a path. */
105
+ export declare function farmPathEntries(paths: FarmPaths): [string, string][];
106
+ export declare function resolveReportedFarmPaths(options: ResolveFarmPathsOptions & {
107
+ role: 'node';
108
+ }): NodeFarmPaths;
109
+ export declare function resolveReportedFarmPaths(options: ResolveFarmPathsOptions & {
110
+ role: 'gateway';
111
+ }): GatewayFarmPaths;
112
+ export declare function resolveReportedFarmPaths(options: ResolveFarmPathsOptions): FarmPaths;
113
+ export interface InstanceConfig {
114
+ layout: 2;
115
+ name?: string;
116
+ dataRoot?: string;
117
+ ports?: FarmInstancePorts;
118
+ }
119
+ export interface FarmInstancePorts {
120
+ gateway?: number;
121
+ }
122
+ /** Validate a target's marker before resolving its paths on the deploying host. */
123
+ export declare function parseFarmInstanceConfig(value: unknown, file: string): InstanceConfig;
124
+ export declare function resolveFarmPaths(options: ResolveFarmPathsOptions & {
125
+ role: 'gateway';
126
+ }): GatewayFarmPaths;
127
+ export declare function resolveFarmPaths(options: ResolveFarmPathsOptions & {
128
+ role: 'node';
129
+ }): NodeFarmPaths;
130
+ export declare function resolveFarmPaths(options: ResolveFarmPathsOptions): FarmPaths;
131
+ export declare function currentFarmPaths(role: 'gateway', env?: FarmPathEnv, checkoutRoot?: string): GatewayFarmPaths;
132
+ export declare function currentFarmPaths(role: 'node', env?: FarmPathEnv, checkoutRoot?: string): NodeFarmPaths;
133
+ /** Older nodes have no report. Keep their shell-expanded legacy paths in one place.
134
+ * Template expansion historically honors WORKSPACE; helper execution does not.
135
+ */
136
+ export declare function legacyRemoteFarmPaths(env?: FarmPathEnv, discoveredCheckout?: string, agentRuntimeScripts?: string): {
137
+ home: string;
138
+ pool: string;
139
+ projects: string;
140
+ code: FarmCodePaths;
141
+ state: {
142
+ runs: string;
143
+ tmp: string;
144
+ };
145
+ };
146
+ /** Validate the optional wire report without trusting a cast at the RPC boundary. */
147
+ export declare function isNodeFarmPaths(value: unknown): value is NodeFarmPaths;
148
+ export {};
149
+ //# sourceMappingURL=farm-paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"farm-paths.d.ts","sourceRoot":"","sources":["../../src/node/farm-paths.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,429 @@
1
+ import { readFileSync, statSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { readLegacyStorageFacts } from './farm-paths-storage.js';
6
+ import { expandHome } from './farmslot-home.js';
7
+ export const FARM_INSTANCE_CLAIM = '.farmslot-instance';
8
+ /** Addressable path leaves, including indexed path arrays. Metadata is never a path. */
9
+ export function farmPathEntries(paths) {
10
+ const roots = Object.fromEntries(Object.entries(paths).filter(([key]) => !['layout', 'instance', 'role', 'envOverrides'].includes(key)));
11
+ function entries(value, prefix) {
12
+ if (typeof value === 'string')
13
+ return [[prefix, value]];
14
+ if (!value || typeof value !== 'object')
15
+ return [];
16
+ return Object.entries(value).flatMap(([key, child]) => entries(child, prefix ? `${prefix}.${key}` : key));
17
+ }
18
+ return entries(roots, '');
19
+ }
20
+ /** Read storage authority outside the resolver before exposing an instance report. */
21
+ export function resolveReportedFarmPaths(options) {
22
+ const paths = resolveFarmPaths(options);
23
+ return paths.layout === 2
24
+ ? paths
25
+ : resolveFarmPaths({ ...options, legacyFacts: readLegacyStorageFacts(paths) });
26
+ }
27
+ function isFarmInstancePorts(value) {
28
+ return (!!value &&
29
+ typeof value === 'object' &&
30
+ !Array.isArray(value) &&
31
+ (!('gateway' in value) ||
32
+ (typeof value.gateway === 'number' &&
33
+ Number.isInteger(value.gateway) &&
34
+ value.gateway > 0 &&
35
+ value.gateway <= 65535)));
36
+ }
37
+ function readInstanceConfig(home) {
38
+ let content;
39
+ try {
40
+ content = readFileSync(path.join(home, 'config', 'instance.json'), 'utf8');
41
+ }
42
+ catch (error) {
43
+ // An absent marker is an unmigrated instance. Other read failures must be visible.
44
+ if (error instanceof Error && 'code' in error && error.code === 'ENOENT')
45
+ return undefined;
46
+ throw error;
47
+ }
48
+ return parseFarmInstanceConfig(JSON.parse(content), path.join(home, 'config', 'instance.json'));
49
+ }
50
+ /** Validate a target's marker before resolving its paths on the deploying host. */
51
+ export function parseFarmInstanceConfig(value, file) {
52
+ if (!value ||
53
+ typeof value !== 'object' ||
54
+ !('layout' in value) ||
55
+ value.layout !== 2 ||
56
+ ('name' in value && (typeof value.name !== 'string' || !value.name.trim())) ||
57
+ ('dataRoot' in value && (typeof value.dataRoot !== 'string' || !value.dataRoot.trim())) ||
58
+ ('ports' in value && !isFarmInstancePorts(value.ports))) {
59
+ throw new Error(`Invalid farm instance config: ${file}`);
60
+ }
61
+ return {
62
+ layout: 2,
63
+ ...('name' in value && typeof value.name === 'string' ? { name: value.name } : {}),
64
+ ...('ports' in value && isFarmInstancePorts(value.ports) ? { ports: value.ports } : {}),
65
+ ...('dataRoot' in value && typeof value.dataRoot === 'string'
66
+ ? { dataRoot: value.dataRoot }
67
+ : {}),
68
+ };
69
+ }
70
+ /**
71
+ * Report one instance's paths without creating directories or discovering stores.
72
+ * Only config/instance.json is read. Legacy selectors remain in place until migration.
73
+ */
74
+ export function resolveFarmPaths(options) {
75
+ const env = options.env ?? process.env;
76
+ const selectedHome = expandHome((options.home ?? env.FARMSLOT_HOME)?.trim() || path.join(homedir(), '.farmslot'));
77
+ const config = options.instanceConfig === undefined
78
+ ? readInstanceConfig(selectedHome)
79
+ : options.instanceConfig;
80
+ const home = config ? path.resolve(selectedHome) : selectedHome;
81
+ const instance = config?.name ?? path.basename(home).replace(/^\./u, '');
82
+ const envOverrides = [];
83
+ // Match each selector's own truthiness/nullish and path handling in legacy mode.
84
+ function override(name, fallback, nullish = false) {
85
+ const value = env[name];
86
+ if (nullish ? value !== undefined : !!value) {
87
+ envOverrides.push(name);
88
+ return value ?? fallback;
89
+ }
90
+ return fallback;
91
+ }
92
+ const atHome = (...parts) => path.join(home, ...parts);
93
+ if (config) {
94
+ const data = config.dataRoot ?? (env.FARMSLOT_DATA?.trim() || atHome('data'));
95
+ const dataRoot = path.resolve(home, expandHome(data));
96
+ const shared = {
97
+ layout: 2,
98
+ instance,
99
+ home,
100
+ envOverrides,
101
+ config: atHome('config'),
102
+ configFiles: { sources: atHome('config', 'sources.json') },
103
+ pool: atHome('pool'),
104
+ projects: atHome('projects'),
105
+ templateProjects: atHome('projects'),
106
+ code: {
107
+ checkout: atHome('code', 'node'),
108
+ agentRuntimeScripts: options.agentRuntimeScripts ??
109
+ atHome('code', 'node', 'packages', 'agent-runtime', 'scripts'),
110
+ templateNode: atHome('code', 'node'),
111
+ node: atHome('code', 'node'),
112
+ cli: atHome('code', 'cli'),
113
+ bin: atHome('code', 'bin'),
114
+ support: atHome('code', 'support'),
115
+ },
116
+ backups: atHome('backups'),
117
+ data: {
118
+ root: dataRoot,
119
+ runBundles: path.join(dataRoot, 'run-bundles'),
120
+ runArchive: path.join(dataRoot, 'run-archive'),
121
+ taskPinsArchive: path.join(dataRoot, 'task-pins-archive'),
122
+ canary: path.join(dataRoot, 'canary'),
123
+ datasets: path.join(dataRoot, 'datasets'),
124
+ },
125
+ };
126
+ const state = {
127
+ runs: atHome('state', 'runs'),
128
+ taskPins: atHome('state', 'task-pins'),
129
+ nativeSessions: atHome('state', 'native-sessions'),
130
+ reviewWorkspaces: atHome('state', 'review-workspaces'),
131
+ sources: atHome('state', 'sources'),
132
+ nodeLink: atHome('state', 'node-link'),
133
+ logs: atHome('state', 'logs'),
134
+ tmp: atHome('state', 'tmp'),
135
+ cache: atHome('state', 'cache'),
136
+ gatewayLock: atHome('state', 'gateway.lock'),
137
+ sessionArchives: atHome('state', 'runs', 'session-archives'),
138
+ episodes: atHome('state', 'episodes'),
139
+ reviewWorkspaceSupport: atHome('state', 'review-workspace-support'),
140
+ browserCache: atHome('state', 'browser-cache'),
141
+ copilotRuntime: atHome('state', 'copilot-runtime'),
142
+ assessmentArtifacts: atHome('state', 'assessment-artifacts'),
143
+ pressureHistory: atHome('state', 'pressure-history.json'),
144
+ intelligenceAudit: atHome('state', 'logs', 'intelligence-actions'),
145
+ feedbackLedger: atHome('state', 'feedback-ledger.json'),
146
+ extraLogDirs: [],
147
+ };
148
+ return options.role === 'gateway'
149
+ ? {
150
+ ...shared,
151
+ role: 'gateway',
152
+ state: {
153
+ ...state,
154
+ backlog: atHome('state', 'backlog'),
155
+ backlogFile: atHome('state', 'backlog', 'backlog.json'),
156
+ roadmap: atHome('state', 'roadmap'),
157
+ workGraphs: atHome('state', 'work-graphs'),
158
+ copilot: atHome('state', 'copilot'),
159
+ assessments: atHome('state', 'assessments'),
160
+ analytics: atHome('state', 'analytics'),
161
+ backlogSpecs: atHome('state', 'backlog', 'specs'),
162
+ refinementPrompts: atHome('state', 'roadmap', 'refinement-prompts'),
163
+ dispatchQueue: atHome('state', 'dispatch-queue.json'),
164
+ statusFile: atHome('state', 'status.json'),
165
+ evalCaps: atHome('state', 'eval-suite-caps.json'),
166
+ observabilityAgreement: atHome('state', 'observability-agreement'),
167
+ backlogRefinementPrompts: atHome('state', 'backlog', 'refinement-prompts'),
168
+ resourceWatchState: atHome('state', 'cache', 'resource-watch-state.json'),
169
+ capabilityStore: atHome('state', 'runtime-capabilities.json'),
170
+ },
171
+ }
172
+ : { ...shared, role: 'node', state };
173
+ }
174
+ // Source and dist have the same depth. No marker scans or workspace-state reads.
175
+ const checkout = options.checkoutRoot ?? (env.FARMSLOT_ROOT || undefined) ?? defaultCheckoutRoot();
176
+ const root = path.resolve(checkout);
177
+ const userHome = options.userHome ?? homedir();
178
+ const pool = override('FARMSLOT_POOL_DIR', path.join(root, 'pool'));
179
+ if (env.FARMSLOT_PROJECTS_DIR)
180
+ envOverrides.push('FARMSLOT_PROJECTS_DIR');
181
+ const runs = override('FARMSLOT_RUNS_DIR', path.join(root, '.runs'));
182
+ const native = path.resolve(override('FARMSLOT_NATIVE_STATE_DIR', atHome('native-sessions'), true));
183
+ const shared = {
184
+ layout: 'legacy',
185
+ instance,
186
+ home,
187
+ envOverrides,
188
+ config: home,
189
+ configFiles: { sources: override('FARMSLOT_SOURCES_CONFIG', atHome('sources.json'), true) },
190
+ pool: env.FARMSLOT_POOL_DIR ? path.resolve(pool) : pool,
191
+ projects: env.FARMSLOT_PROJECTS_DIR
192
+ ? path.resolve(env.FARMSLOT_PROJECTS_DIR)
193
+ : path.join(root, 'projects'),
194
+ templateProjects: path.join(root, 'projects'),
195
+ code: {
196
+ checkout: root,
197
+ agentRuntimeScripts: options.agentRuntimeScripts ??
198
+ path.join(root, installedProtocol()
199
+ ? 'node_modules/@farmslot/agent-runtime/scripts'
200
+ : 'packages/agent-runtime/scripts'),
201
+ templateNode: env.FARMSLOT_WORKSPACE
202
+ ? path.resolve(expandHome(env.FARMSLOT_WORKSPACE))
203
+ : root,
204
+ node: env.FARMSLOT_WORKSPACE ? path.resolve(expandHome(env.FARMSLOT_WORKSPACE)) : root,
205
+ cli: path.join(userHome, '.local', 'share', 'farmslot-cli'),
206
+ bin: path.join(userHome, '.local', 'bin'),
207
+ support: path.join(homedir(), 'farmslot-node', 'support'),
208
+ },
209
+ backups: path.join(root, '.backups'),
210
+ data: {
211
+ root: home,
212
+ runBundles: options.legacyFacts?.runBundles ??
213
+ path.join(options.home !== undefined || env.FARMSLOT_HOME || !env.FARMSLOT_RUNS_DIR
214
+ ? home
215
+ : path.join(runs, 'bundle-home'), 'run-bundles'),
216
+ runArchive: override('FARMSLOT_RUN_ARCHIVE_ROOT', atHome('run-archive'), true),
217
+ taskPinsArchive: atHome('task-pins-archive'),
218
+ canary: override('CANARY_HOME', path.join(homedir(), 'farmslot-canary')),
219
+ datasets: atHome('datasets'),
220
+ },
221
+ };
222
+ const state = {
223
+ runs,
224
+ taskPins: path.resolve(home, 'task-pins'),
225
+ nativeSessions: native,
226
+ reviewWorkspaces: path.join(path.dirname(native), 'review-workspaces'),
227
+ sources: atHome('sources'),
228
+ nodeLink: atHome('node-link'),
229
+ logs: env.FARMSLOT_LOG_DIR
230
+ ? path.resolve(override('FARMSLOT_LOG_DIR', atHome('logs')))
231
+ : atHome('logs'),
232
+ tmp: '/tmp/farmslot-prepare',
233
+ cache: path.join(override('FARMSLOT_DIR', root), '.farm-cache'),
234
+ gatewayLock: path.resolve(root, '.runs', `gateway-${Number(env.GATEWAY_PORT) || 7777}.pid`),
235
+ sessionArchives: path.join(runs, 'session-archives'),
236
+ episodes: path.join(runs, 'episodes'),
237
+ reviewWorkspaceSupport: path.join(runs, 'review-workspace-support'),
238
+ browserCache: env.RECIPE_HARNESS_BROWSER_CACHE
239
+ ? path.resolve(override('RECIPE_HARNESS_BROWSER_CACHE', ''))
240
+ : path.join(env.XDG_CACHE_HOME || path.join(homedir(), '.cache'), 'farmslot', 'browser-probe.json'),
241
+ copilotRuntime: atHome('copilot-runtime'),
242
+ assessmentArtifacts: home,
243
+ pressureHistory: atHome('state', 'pressure-history.json'),
244
+ intelligenceAudit: override('FARMSLOT_INTELLIGENCE_AUDIT_DIR', atHome('logs', 'intelligence-actions'), true),
245
+ feedbackLedger: env.FARMSLOT_FEEDBACK_LEDGER?.trim()
246
+ ? override('FARMSLOT_FEEDBACK_LEDGER', '').trim()
247
+ : atHome('state', 'feedback-ledger.json'),
248
+ extraLogDirs: override('FARMSLOT_EXTRA_LOG_DIRS', '')
249
+ .split(path.delimiter)
250
+ .map((entry) => entry.trim())
251
+ .filter(Boolean)
252
+ .map((entry) => path.resolve(entry)),
253
+ };
254
+ return options.role === 'gateway'
255
+ ? {
256
+ ...shared,
257
+ role: 'gateway',
258
+ state: {
259
+ ...state,
260
+ backlog: override('FARMSLOT_BACKLOG_FILE', path.join(root, '.backlog.json')),
261
+ backlogFile: env.FARMSLOT_BACKLOG_FILE || path.join(root, '.backlog.json'),
262
+ roadmap: override('FARMSLOT_ROADMAP_DIR', path.join(root, '.roadmap'), true),
263
+ workGraphs: override('FARMSLOT_WORK_GRAPH_DIR', path.join(root, '.work-graphs')),
264
+ copilot: override('FARMSLOT_COPILOT_DIR', path.join(root, 'copilot'), true),
265
+ assessments: atHome('assessments'),
266
+ analytics: override('FARMSLOT_ANALYTICS_DIR', path.join(root, '.runs', 'analytics')),
267
+ backlogSpecs: override('FARMSLOT_BACKLOG_SPEC_DIR', path.join(root, '.backlog', 'specs'), true),
268
+ refinementPrompts: override('FARMSLOT_ROADMAP_REFINEMENT_PROMPT_DIR', path.join(env.FARMSLOT_ROADMAP_DIR ?? path.join(root, '.roadmap'), 'refinement-prompts'), true),
269
+ dispatchQueue: override('FARMSLOT_DISPATCH_QUEUE_FILE', path.join(root, '.dispatch-queue.json')),
270
+ statusFile: path.join(root, '.farm-status.json'),
271
+ evalCaps: override('FARMSLOT_EVAL_SUITE_CAP_FILE', path.join(root, '.eval-suite-caps.json')),
272
+ observabilityAgreement: override('FARMSLOT_OBSERVABILITY_AGREEMENT_DIR', path.join(root, '.runs', 'observability-agreement')),
273
+ backlogRefinementPrompts: override('FARMSLOT_BACKLOG_REFINEMENT_PROMPT_DIR', path.join(root, '.backlog', 'refinement-prompts'), true),
274
+ resourceWatchState: override('FARMSLOT_RESOURCE_WATCH_STATE_FILE', path.join(root, '.farm-cache', 'resource-watch-state.json'), true),
275
+ capabilityStore: override('FARMSLOT_CAPABILITY_STORE_FILE', path.join(env.FARMSLOT_ROOT?.trim() || process.cwd(), '.runs', `runtime-capabilities-${env.GATEWAY_PORT?.trim() || '7777'}.json`)),
276
+ },
277
+ }
278
+ : { ...shared, role: 'node', state };
279
+ }
280
+ // A process may switch homes during a test or CLI invocation. Include the marker timestamp
281
+ // and environment in the key so neither a migration nor a changed override leaves stale roots.
282
+ const processPaths = new Map();
283
+ const warnedOverrides = new Set();
284
+ export function currentFarmPaths(role, env = process.env, checkoutRoot) {
285
+ const home = expandHome(env.FARMSLOT_HOME?.trim() || path.join(homedir(), '.farmslot'));
286
+ let marker = 'absent';
287
+ try {
288
+ const info = statSync(path.join(home, 'config', 'instance.json'));
289
+ marker = `${info.mtimeMs}:${info.ctimeMs}:${info.size}`;
290
+ }
291
+ catch (error) {
292
+ // A missing marker selects legacy; permission and other filesystem errors must surface.
293
+ if (!(error instanceof Error && 'code' in error && error.code === 'ENOENT'))
294
+ throw error;
295
+ }
296
+ const selectedEnv = Object.entries(env).filter(([name]) => name.startsWith('FARMSLOT_') ||
297
+ name === 'GATEWAY_PORT' ||
298
+ name === 'CANARY_HOME' ||
299
+ name === 'RECIPE_HARNESS_BROWSER_CACHE' ||
300
+ name === 'XDG_CACHE_HOME');
301
+ const key = JSON.stringify([
302
+ role,
303
+ home,
304
+ homedir(),
305
+ marker,
306
+ checkoutRoot,
307
+ process.cwd(),
308
+ selectedEnv,
309
+ ]);
310
+ let paths = processPaths.get(key);
311
+ if (!paths) {
312
+ paths = resolveFarmPaths({ role, env, checkoutRoot });
313
+ processPaths.set(key, paths);
314
+ }
315
+ for (const name of paths.envOverrides) {
316
+ if (warnedOverrides.has(name))
317
+ continue;
318
+ warnedOverrides.add(name);
319
+ console.warn(`[farm-paths] ${name} is deprecated; configure instance.json before migrating to layout 2`);
320
+ }
321
+ return paths;
322
+ }
323
+ /** Older nodes have no report. Keep their shell-expanded legacy paths in one place.
324
+ * Template expansion historically honors WORKSPACE; helper execution does not.
325
+ */
326
+ export function legacyRemoteFarmPaths(env = {}, discoveredCheckout, agentRuntimeScripts) {
327
+ const node = discoveredCheckout ?? path.posix.join(env.HOME?.trim() || '~', 'farmslot-node');
328
+ return {
329
+ home: env.FARMSLOT_HOME?.trim() || '~/.farmslot',
330
+ pool: env.FARMSLOT_POOL_DIR || path.posix.join(node, 'pool'),
331
+ projects: env.FARMSLOT_PROJECTS_DIR || path.posix.join(node, 'projects'),
332
+ code: {
333
+ checkout: node,
334
+ agentRuntimeScripts: agentRuntimeScripts ??
335
+ path.posix.join(node, 'node_modules/@farmslot/agent-runtime/scripts'),
336
+ node,
337
+ templateNode: env.FARMSLOT_WORKSPACE?.trim() || node,
338
+ cli: '~/.local/share/farmslot-cli',
339
+ bin: '~/.local/bin',
340
+ // Remote bundles are deployed beside the selected node checkout; the local
341
+ // legacy selector uses the user-home node install even from an operator checkout.
342
+ support: path.posix.join(node, 'support'),
343
+ },
344
+ state: {
345
+ runs: env.FARMSLOT_RUNS_DIR || path.posix.join(node, '.runs'),
346
+ tmp: '/tmp/farmslot-prepare',
347
+ },
348
+ };
349
+ }
350
+ /** Validate the optional wire report without trusting a cast at the RPC boundary. */
351
+ export function isNodeFarmPaths(value) {
352
+ if (!value || typeof value !== 'object' || Array.isArray(value))
353
+ return false;
354
+ const fields = Object.fromEntries(Object.entries(value));
355
+ const stringFields = (item, names) => {
356
+ if (!item || typeof item !== 'object' || Array.isArray(item))
357
+ return false;
358
+ return names.every((name) => {
359
+ const field = Reflect.get(item, name);
360
+ return typeof field === 'string' && !field.includes('\0');
361
+ });
362
+ };
363
+ const strings = (item) => Array.isArray(item) && item.every((entry) => typeof entry === 'string');
364
+ return (fields.role === 'node' &&
365
+ (fields.layout === 'legacy' || fields.layout === 2) &&
366
+ stringFields(value, [
367
+ 'instance',
368
+ 'home',
369
+ 'config',
370
+ 'pool',
371
+ 'projects',
372
+ 'templateProjects',
373
+ 'backups',
374
+ ]) &&
375
+ stringFields(fields.configFiles, ['sources']) &&
376
+ stringFields(fields.code, [
377
+ 'checkout',
378
+ 'agentRuntimeScripts',
379
+ 'templateNode',
380
+ 'node',
381
+ 'cli',
382
+ 'bin',
383
+ 'support',
384
+ ]) &&
385
+ stringFields(fields.data, [
386
+ 'root',
387
+ 'runBundles',
388
+ 'runArchive',
389
+ 'taskPinsArchive',
390
+ 'canary',
391
+ 'datasets',
392
+ ]) &&
393
+ stringFields(fields.state, [
394
+ 'runs',
395
+ 'taskPins',
396
+ 'nativeSessions',
397
+ 'reviewWorkspaces',
398
+ 'sources',
399
+ 'nodeLink',
400
+ 'logs',
401
+ 'tmp',
402
+ 'cache',
403
+ 'gatewayLock',
404
+ 'sessionArchives',
405
+ 'episodes',
406
+ 'reviewWorkspaceSupport',
407
+ 'browserCache',
408
+ 'copilotRuntime',
409
+ 'assessmentArtifacts',
410
+ 'pressureHistory',
411
+ 'intelligenceAudit',
412
+ 'feedbackLedger',
413
+ ]) &&
414
+ strings(fields.envOverrides) &&
415
+ fields.state !== null &&
416
+ typeof fields.state === 'object' &&
417
+ strings(Reflect.get(fields.state, 'extraLogDirs')));
418
+ }
419
+ // A standalone service installs the protocol as a scoped dependency. Its package
420
+ // location tells us the installation root without reading a second marker or package.
421
+ function installedProtocol() {
422
+ const packageRoot = path.resolve(fileURLToPath(new URL('../../', import.meta.url)));
423
+ return (path.basename(path.dirname(packageRoot)) === '@farmslot' &&
424
+ path.basename(path.dirname(path.dirname(packageRoot))) === 'node_modules');
425
+ }
426
+ function defaultCheckoutRoot() {
427
+ return fileURLToPath(new URL(installedProtocol() ? '../../../../../' : '../../../../', import.meta.url));
428
+ }
429
+ //# sourceMappingURL=farm-paths.js.map