Skip to content

Commit a5443a1

Browse files
committed
feat: enhance apply and pull logic with improved handling of force option and assistant ID resolution
- Updated the `apply.ts` script to simplify argument handling for pull commands, ensuring local changes are preserved. - Enhanced the pull functionality in `pull.ts` to delete local files for resources removed from the platform when the force option is used. - Implemented resolution of `assistantId` in `assistantOverrides` for tools, ensuring correct mapping during resource processing. - Improved logging to indicate updates and removals, providing clearer feedback during operations.
1 parent e4d97ef commit a5443a1

4 files changed

Lines changed: 83 additions & 40 deletions

File tree

src/apply.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,7 @@ async function main(): Promise<void> {
2929
const allArgs = process.argv.slice(3);
3030
const hasForce = allArgs.includes("--force");
3131

32-
// Pull never gets --force (apply's pull should always preserve local changes/deletions)
33-
const pullArgs = allArgs.filter(a => a !== "--force").join(" ");
32+
const pullArgs = allArgs.join(" ");
3433
const pushArgs = allArgs.join(" ");
3534

3635
if (!env || !VALID_ENVIRONMENTS.includes(env as typeof VALID_ENVIRONMENTS[number])) {
@@ -54,7 +53,7 @@ async function main(): Promise<void> {
5453
}
5554
console.log("═══════════════════════════════════════════════════════════════\n");
5655

57-
// Step 1: Pull (never forced — always preserves local deletions/changes)
56+
// Step 1: Pull (--force forwarded: platform becomes source of truth, deletes orphaned local files)
5857
const pullCmd = `npx tsx src/pull.ts ${env} ${pullArgs}`.trim();
5958
const pullExit = runPassthrough(pullCmd);
6059
if (pullExit !== 0) {

src/pull.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { execSync } from "child_process";
22
import { existsSync, readdirSync } from "fs";
3-
import { mkdir, writeFile } from "fs/promises";
3+
import { mkdir, writeFile, unlink } from "fs/promises";
44
import { join, dirname, relative } from "path";
55
import { stringify } from "yaml";
66
import { VAPI_ENV, VAPI_BASE_URL, VAPI_TOKEN, RESOURCES_DIR, BASE_DIR, APPLY_FILTER } from "./config.ts";
@@ -335,6 +335,21 @@ function resolveReferencesToResourceIds(
335335
return dest;
336336
});
337337
}
338+
// Resolve assistantId in assistantOverrides["tools:append"][].destinations[]
339+
const overrides = resolvedMember.assistantOverrides as Record<string, unknown> | undefined;
340+
const toolsAppend = overrides?.["tools:append"] as Record<string, unknown>[] | undefined;
341+
if (Array.isArray(toolsAppend)) {
342+
for (const tool of toolsAppend) {
343+
if (Array.isArray(tool.destinations)) {
344+
tool.destinations = (tool.destinations as Record<string, unknown>[]).map((dest) => {
345+
if (typeof dest.assistantId === "string") {
346+
return { ...dest, assistantId: assistantsMap.get(dest.assistantId) ?? dest.assistantId };
347+
}
348+
return dest;
349+
});
350+
}
351+
}
352+
}
338353
return resolvedMember;
339354
});
340355
}
@@ -553,6 +568,25 @@ export async function pullResourceType(
553568
newStateSection[resourceId] = resource.id;
554569
}
555570

571+
// In force mode, delete local files for resources removed from the platform
572+
if (force) {
573+
const oldStateSection = state[resourceType];
574+
for (const [resourceId] of Object.entries(oldStateSection)) {
575+
if (newStateSection[resourceId]) continue;
576+
const folderPath = FOLDER_MAP[resourceType];
577+
const dir = join(RESOURCES_DIR, folderPath);
578+
for (const ext of [".md", ".yml", ".yaml"]) {
579+
const filePath = join(dir, `${resourceId}${ext}`);
580+
if (existsSync(filePath)) {
581+
await unlink(filePath);
582+
const relPath = relative(BASE_DIR, filePath);
583+
console.log(` 🗑️ ${resourceId} -> ${relPath} (removed from platform)`);
584+
break;
585+
}
586+
}
587+
}
588+
}
589+
556590
// Update state with new mappings
557591
state[resourceType] = newStateSection;
558592

src/push.ts

Lines changed: 15 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -400,12 +400,13 @@ interface DependencyContext {
400400
}
401401

402402
async function ensureToolExists(toolId: string, ctx: DependencyContext): Promise<void> {
403-
if (UUID_REGEX.test(toolId) || ctx.state.tools[toolId] || ctx.autoApplied.has(`tools:${toolId}`)) return;
403+
if (UUID_REGEX.test(toolId) || ctx.autoApplied.has(`tools:${toolId}`)) return;
404404

405405
const tool = ctx.allTools.find(t => t.resourceId === toolId);
406406
if (!tool) return;
407407

408-
console.log(` 📦 Auto-applying dependency → tool: ${toolId}`);
408+
const isUpdate = !!ctx.state.tools[toolId];
409+
console.log(` 📦 Auto-applying dependency → tool: ${toolId}${isUpdate ? " (update)" : ""}`);
409410
try {
410411
const uuid = await applyTool(tool, ctx.state);
411412
ctx.state.tools[tool.resourceId] = uuid;
@@ -419,12 +420,13 @@ async function ensureToolExists(toolId: string, ctx: DependencyContext): Promise
419420
}
420421

421422
async function ensureStructuredOutputExists(outputId: string, ctx: DependencyContext): Promise<void> {
422-
if (UUID_REGEX.test(outputId) || ctx.state.structuredOutputs[outputId] || ctx.autoApplied.has(`structuredOutputs:${outputId}`)) return;
423+
if (UUID_REGEX.test(outputId) || ctx.autoApplied.has(`structuredOutputs:${outputId}`)) return;
423424

424425
const output = ctx.allStructuredOutputs.find(o => o.resourceId === outputId);
425426
if (!output) return;
426427

427-
console.log(` 📦 Auto-applying dependency → structured output: ${outputId}`);
428+
const isUpdate = !!ctx.state.structuredOutputs[outputId];
429+
console.log(` 📦 Auto-applying dependency → structured output: ${outputId}${isUpdate ? " (update)" : ""}`);
428430
try {
429431
const uuid = await applyStructuredOutput(output, ctx.state);
430432
ctx.state.structuredOutputs[output.resourceId] = uuid;
@@ -437,55 +439,32 @@ async function ensureStructuredOutputExists(outputId: string, ctx: DependencyCon
437439
}
438440
}
439441

440-
async function ensureAssistantDepsExist(assistantId: string, ctx: DependencyContext): Promise<boolean> {
441-
if (UUID_REGEX.test(assistantId)) return false;
442+
async function ensureAssistantDepsExist(assistantId: string, ctx: DependencyContext): Promise<void> {
443+
if (UUID_REGEX.test(assistantId)) return;
442444

443445
const assistant = ctx.allAssistants.find(a => a.resourceId === assistantId);
444-
if (!assistant) return false;
446+
if (!assistant) return;
445447

446448
const refs = extractReferencedIds(assistant.data as Record<string, unknown>);
447-
let depsCreated = false;
448449

449450
for (const toolId of refs.tools) {
450-
if (!UUID_REGEX.test(toolId) && !ctx.state.tools[toolId]) {
451-
await ensureToolExists(toolId, ctx);
452-
if (ctx.state.tools[toolId]) depsCreated = true;
453-
}
451+
await ensureToolExists(toolId, ctx);
454452
}
455453
for (const outputId of refs.structuredOutputs) {
456-
if (!UUID_REGEX.test(outputId) && !ctx.state.structuredOutputs[outputId]) {
457-
await ensureStructuredOutputExists(outputId, ctx);
458-
if (ctx.state.structuredOutputs[outputId]) depsCreated = true;
459-
}
454+
await ensureStructuredOutputExists(outputId, ctx);
460455
}
461-
462-
return depsCreated;
463456
}
464457

465458
async function ensureAssistantExists(assistantId: string, ctx: DependencyContext): Promise<void> {
466-
if (UUID_REGEX.test(assistantId)) return;
467-
468-
// Always resolve tool/SO deps, even if the assistant already exists in state
469-
const depsCreated = await ensureAssistantDepsExist(assistantId, ctx);
470-
471-
// Assistant already on platform — update it if we just created missing deps
472-
if (ctx.state.assistants[assistantId]) {
473-
if (depsCreated) {
474-
const assistant = ctx.allAssistants.find(a => a.resourceId === assistantId);
475-
if (assistant) {
476-
console.log(` 🔄 Updating assistant with new dependencies: ${assistantId}`);
477-
await applyAssistant(assistant, ctx.state);
478-
}
479-
}
480-
return;
481-
}
459+
if (UUID_REGEX.test(assistantId) || ctx.autoApplied.has(`assistants:${assistantId}`)) return;
482460

483-
if (ctx.autoApplied.has(`assistants:${assistantId}`)) return;
461+
await ensureAssistantDepsExist(assistantId, ctx);
484462

485463
const assistant = ctx.allAssistants.find(a => a.resourceId === assistantId);
486464
if (!assistant) return;
487465

488-
console.log(` 📦 Auto-applying dependency → assistant: ${assistantId}`);
466+
const isUpdate = !!ctx.state.assistants[assistantId];
467+
console.log(` 📦 Auto-applying dependency → assistant: ${assistantId}${isUpdate ? " (update)" : ""}`);
489468
try {
490469
const uuid = await applyAssistant(assistant, ctx.state);
491470
ctx.state.assistants[assistant.resourceId] = uuid;

src/resolver.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,23 @@ export function resolveReferences(
262262
}
263263
}
264264
}
265+
// Resolve assistantId in assistantOverrides["tools:append"][].destinations[]
266+
const overrides = member.assistantOverrides as Record<string, unknown> | undefined;
267+
const toolsAppend = overrides?.["tools:append"] as Record<string, unknown>[] | undefined;
268+
if (Array.isArray(toolsAppend)) {
269+
for (const tool of toolsAppend) {
270+
if (Array.isArray(tool.destinations)) {
271+
for (const dest of tool.destinations as Record<string, unknown>[]) {
272+
if (typeof dest.assistantId === "string") {
273+
const resolvedId = resolveAssistantId(dest.assistantId, state);
274+
if (resolvedId) {
275+
dest.assistantId = resolvedId;
276+
}
277+
}
278+
}
279+
}
280+
}
281+
}
265282
}
266283
}
267284

@@ -401,6 +418,20 @@ export function extractReferencedIds(data: Record<string, unknown>): ExtractedRe
401418
}
402419
}
403420
}
421+
// Check assistantOverrides["tools:append"][].destinations[].assistantId
422+
const overrides = member.assistantOverrides as Record<string, unknown> | undefined;
423+
const toolsAppend = overrides?.["tools:append"] as Record<string, unknown>[] | undefined;
424+
if (Array.isArray(toolsAppend)) {
425+
for (const tool of toolsAppend) {
426+
if (Array.isArray(tool.destinations)) {
427+
for (const dest of tool.destinations as Record<string, unknown>[]) {
428+
if (typeof dest.assistantId === "string") {
429+
assistants.push(cleanId(dest.assistantId));
430+
}
431+
}
432+
}
433+
}
434+
}
404435
}
405436
}
406437

0 commit comments

Comments
 (0)