- Refactor monolithic index.js (2646 lines) into src/{webex,integrations,
cards,flows,commands,services} modules; replace node-fetch/form-data
with native fetch/FormData; move all secrets to .env via dotenv
- Add dockerized remote SIW agent (docker/remote-agent/) with cross-arch
buildx packaging (arm64 Mac -> linux/amd64), idempotent install.sh
deploy bundle, and docker-free ZIP inspector for arch verification
- Bot hosts a WebSocket server; agent proxies SIW requests with a
per-request insecure:true flag, replacing the process-wide
NODE_TLS_REJECT_UNAUTHORIZED bypass
- Add ESLint flat config + Prettier, rewrite Dockerfile as non-root
multi-stage node:22-alpine build, README covering setup / deploy /
remote agent workflow
- Fix parseStoreArg to read trigger.prompt correctly (was indexing past
the framework's post-match slice); register /help as regex (string
matcher only compares the first token); switch catch-all to /.+/
(previous /.*/gim was stateful due to the g flag); remove
/fixDisplayNames command and its flow/card
Co-authored-by: Cursor <cursoragent@cursor.com>
75 lines
2.8 KiB
JavaScript
75 lines
2.8 KiB
JavaScript
import {
|
|
addPhoneNumbersToLocation,
|
|
findWebexLocation,
|
|
updateLocationCallingIdentity,
|
|
} from '../webex/locations.js';
|
|
import { findLocationAnnouncements } from '../webex/announcements.js';
|
|
import { createStoreAutoAttendant } from '../webex/autoAttendants.js';
|
|
import { normalizeStoreUserLicenses } from '../webex/licensing.js';
|
|
import {
|
|
updateUserCallExperience,
|
|
updateUserCallerId,
|
|
updateUserExtension,
|
|
updateUserVoicemailSettings,
|
|
} from '../webex/users.js';
|
|
import { CALLER_ID } from '../constants.js';
|
|
import { logger } from '../logger.js';
|
|
import { runStep } from './stepRunner.js';
|
|
|
|
/**
|
|
* "Migrate store" — finish the cut-over for a store that was previously staged.
|
|
* Attach the phone number, set caller ID, create the auto-attendant, and clean
|
|
* up the user's licensing/voicemail.
|
|
*/
|
|
export async function migrateStoreLocation(bot, locationInfo, userInfo) {
|
|
logger.info(`Migrating location ${locationInfo.name}`);
|
|
const matches = await findWebexLocation(locationInfo.name);
|
|
if (!matches.length) {
|
|
throw new Error(
|
|
`No existing Webex location found for ${locationInfo.name}. Did you stage first?`,
|
|
);
|
|
}
|
|
const location = matches[0];
|
|
|
|
await runStep(bot, 'Added phone numbers to location', () =>
|
|
addPhoneNumbersToLocation(location, locationInfo.phoneNumber),
|
|
);
|
|
await runStep(bot, 'Updated location Webex Calling Details', () =>
|
|
updateLocationCallingIdentity(location, locationInfo.phoneNumber),
|
|
);
|
|
await runStep(bot, 'Updated user extension', () =>
|
|
updateUserExtension(userInfo, locationInfo.extension),
|
|
);
|
|
await runStep(bot, 'Updated user call application experience', () =>
|
|
updateUserCallExperience(userInfo),
|
|
);
|
|
await runStep(bot, 'Updated user Caller ID Information', () =>
|
|
updateUserCallerId(userInfo, locationInfo.extension, locationInfo.phoneNumber, {
|
|
locationFallbackNumber: CALLER_ID.locationFallbackNumber,
|
|
customExternalCallerIdName: CALLER_ID.customExternalCallerIdName,
|
|
}),
|
|
);
|
|
await runStep(bot, 'Updated user voicemail settings', () =>
|
|
updateUserVoicemailSettings(userInfo),
|
|
);
|
|
|
|
const announcements = await runStep(bot, 'Located store announcement', () =>
|
|
findLocationAnnouncements(location),
|
|
);
|
|
if (announcements && announcements[0]) {
|
|
location.announcementId = announcements[0].id;
|
|
await runStep(bot, 'Created auto attendant', () =>
|
|
createStoreAutoAttendant(location, locationInfo),
|
|
);
|
|
} else {
|
|
bot.say(
|
|
'markdown',
|
|
"<blockquote class='failure'>Skipping auto attendant: no location announcement found.</blockquote>",
|
|
);
|
|
}
|
|
|
|
await runStep(bot, 'Fixed Licenses', () => normalizeStoreUserLicenses(userInfo));
|
|
|
|
bot.say('Migration Complete!');
|
|
return location;
|
|
}
|