@adaptic/maestro 1.8.0 → 1.8.1
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/package.json
CHANGED
|
@@ -36,14 +36,38 @@ MAESTRO_PLIST_ARCH_VERSION="1"
|
|
|
36
36
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
37
37
|
AGENT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
38
38
|
PLIST_DIR="$SCRIPT_DIR/plists"
|
|
39
|
-
AGENT_CONFIG="$AGENT_DIR/config/agent.ts"
|
|
40
39
|
|
|
41
|
-
#
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
# Resolve the agent's first name. Order of preference:
|
|
41
|
+
# 1. config/agent.json — single source of truth (SOT) introduced when
|
|
42
|
+
# maestro split identity out of TypeScript into JSON. Use jq if
|
|
43
|
+
# available, otherwise a small awk fallback.
|
|
44
|
+
# 2. config/agent.ts — legacy path; only works if firstName is
|
|
45
|
+
# defined inline (string literal), not as a type-only declaration
|
|
46
|
+
# like `firstName: string;`.
|
|
47
|
+
# 3. Directory name — last-resort fallback (e.g. ~/ravi-ai → ravi).
|
|
48
|
+
AGENT_JSON="$AGENT_DIR/config/agent.json"
|
|
49
|
+
AGENT_TS="$AGENT_DIR/config/agent.ts"
|
|
50
|
+
AGENT_FIRST=""
|
|
51
|
+
|
|
52
|
+
if [ -z "$AGENT_FIRST" ] && [ -f "$AGENT_JSON" ]; then
|
|
53
|
+
if command -v jq >/dev/null 2>&1; then
|
|
54
|
+
AGENT_FIRST=$(jq -r '.firstName // empty' "$AGENT_JSON" 2>/dev/null | tr '[:upper:]' '[:lower:]')
|
|
55
|
+
else
|
|
56
|
+
# Awk fallback — handle "firstName": "value" with optional whitespace
|
|
57
|
+
AGENT_FIRST=$(awk -F'"' '/"firstName"[[:space:]]*:/ { print tolower($4); exit }' "$AGENT_JSON")
|
|
58
|
+
fi
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
if [ -z "$AGENT_FIRST" ] && [ -f "$AGENT_TS" ]; then
|
|
62
|
+
# Only accept a quoted inline value — `firstName: string;` (type
|
|
63
|
+
# declaration) must not match. The grep requires a quote on the line.
|
|
64
|
+
AGENT_FIRST=$(grep "firstName:[[:space:]]*['\"]" "$AGENT_TS" \
|
|
65
|
+
| head -1 \
|
|
66
|
+
| sed "s/.*firstName:[[:space:]]*['\"]//; s/['\"].*//" \
|
|
67
|
+
| tr '[:upper:]' '[:lower:]')
|
|
44
68
|
fi
|
|
45
69
|
|
|
46
|
-
# Fall back to directory name if config not set or UNCONFIGURED
|
|
70
|
+
# Fall back to directory name if config not set or UNCONFIGURED.
|
|
47
71
|
if [ -z "$AGENT_FIRST" ] || [ "$AGENT_FIRST" = "unconfigured" ]; then
|
|
48
72
|
AGENT_FIRST=$(basename "$AGENT_DIR" | sed 's/-ai$//')
|
|
49
73
|
fi
|
|
@@ -167,6 +167,75 @@ test("daemon plist remains a KeepAlive job (not a cadence enqueue)", async () =>
|
|
|
167
167
|
} finally { await rmRoot(root); }
|
|
168
168
|
});
|
|
169
169
|
|
|
170
|
+
test("generator reads firstName from agent.json when agent.ts is type-only (SOT layout)", async () => {
|
|
171
|
+
// Reproduce the SOT layout where agent.ts only declares interfaces
|
|
172
|
+
// (containing `firstName: string;` as a type, not a value) and the
|
|
173
|
+
// actual identity lives in agent.json. The legacy resolver matched the
|
|
174
|
+
// interface line and produced labels like
|
|
175
|
+
// `ai.adaptic. firstname: string;-…` — guard against regression.
|
|
176
|
+
const root = join(
|
|
177
|
+
tmpdir(),
|
|
178
|
+
`plist-sot-test-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
|
|
179
|
+
);
|
|
180
|
+
await fsp.mkdir(root, { recursive: true });
|
|
181
|
+
// Manually wire the same fixture-builder used by other tests, except
|
|
182
|
+
// overwrite agent.ts to look like a type-only TypeScript file and add
|
|
183
|
+
// a real agent.json.
|
|
184
|
+
await fsp.mkdir(join(root, "config"), { recursive: true });
|
|
185
|
+
writeFileSync(
|
|
186
|
+
join(root, "config/agent.ts"),
|
|
187
|
+
`// Type declarations only — value lives in agent.json
|
|
188
|
+
export interface PrincipalConfig { firstName: string; }
|
|
189
|
+
export interface AgentConfig { firstName: string; }
|
|
190
|
+
`,
|
|
191
|
+
);
|
|
192
|
+
writeFileSync(
|
|
193
|
+
join(root, "config/agent.json"),
|
|
194
|
+
JSON.stringify({ firstName: "Sigrid", lastName: "Test" }) + "\n",
|
|
195
|
+
);
|
|
196
|
+
writeFileSync(join(root, "package.json"), '{"name":"t"}');
|
|
197
|
+
|
|
198
|
+
await fsp.mkdir(join(root, "scripts/local-triggers"), { recursive: true });
|
|
199
|
+
await fsp.mkdir(join(root, "scripts/daemon"), { recursive: true });
|
|
200
|
+
await fsp.mkdir(join(root, "scripts/cadence"), { recursive: true });
|
|
201
|
+
// Same set of helper scripts as makeAgent().
|
|
202
|
+
const { copyFileSync } = await import("node:fs");
|
|
203
|
+
copyFileSync(GENERATOR, join(root, "scripts/local-triggers/generate-plists.sh"));
|
|
204
|
+
copyFileSync(
|
|
205
|
+
join(MAESTRO_ROOT, "scripts/local-triggers/run-trigger.sh"),
|
|
206
|
+
join(root, "scripts/local-triggers/run-trigger.sh"),
|
|
207
|
+
);
|
|
208
|
+
copyFileSync(
|
|
209
|
+
join(MAESTRO_ROOT, "scripts/daemon/launchd-wrapper.sh"),
|
|
210
|
+
join(root, "scripts/daemon/launchd-wrapper.sh"),
|
|
211
|
+
);
|
|
212
|
+
copyFileSync(
|
|
213
|
+
join(MAESTRO_ROOT, "scripts/daemon/launchd-wrapper-generic.sh"),
|
|
214
|
+
join(root, "scripts/daemon/launchd-wrapper-generic.sh"),
|
|
215
|
+
);
|
|
216
|
+
copyFileSync(
|
|
217
|
+
join(MAESTRO_ROOT, "scripts/cadence/launchd-cadence-wrapper.sh"),
|
|
218
|
+
join(root, "scripts/cadence/launchd-cadence-wrapper.sh"),
|
|
219
|
+
);
|
|
220
|
+
copyFileSync(
|
|
221
|
+
join(MAESTRO_ROOT, "scripts/cadence/enqueue-cadence-tick.mjs"),
|
|
222
|
+
join(root, "scripts/cadence/enqueue-cadence-tick.mjs"),
|
|
223
|
+
);
|
|
224
|
+
await fsp.chmod(join(root, "scripts/local-triggers/generate-plists.sh"), 0o755);
|
|
225
|
+
|
|
226
|
+
try {
|
|
227
|
+
const r = runGenerator(root);
|
|
228
|
+
assert.equal(r.status, 0, r.stderr);
|
|
229
|
+
const plists = listPlists(root);
|
|
230
|
+
assert.ok(plists.length >= 1);
|
|
231
|
+
for (const p of plists) {
|
|
232
|
+
assert.match(p, /^ai\.adaptic\.sigrid-/, `expected "sigrid" label, got: ${p}`);
|
|
233
|
+
assert.ok(!p.includes("string"),
|
|
234
|
+
`label leaked the TypeScript type: ${p}`);
|
|
235
|
+
}
|
|
236
|
+
} finally { await rmRoot(root); }
|
|
237
|
+
});
|
|
238
|
+
|
|
170
239
|
test("regeneration clears stale plists", async () => {
|
|
171
240
|
const root = await makeAgent("frank");
|
|
172
241
|
try {
|