@jacebenson/jsn 4.3.7-dev.26 → 4.3.7-dev.27

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jacebenson/jsn",
3
- "version": "4.3.7-dev.26",
3
+ "version": "4.3.7-dev.27",
4
4
  "description": "A command-line interface for ServiceNow that follows the Unix philosophy: simple, composable, and scriptable.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,9 +1,11 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
1
3
  import { input, password as passwordPrompt, select } from '@inquirer/prompts';
2
4
  import { confirmDelete, formatRecordForDisplay, getStringField, interactiveList, canPrompt } from '../helpers.js';
3
5
  import { FormatAuto } from '../output.js';
4
6
  import { declareCapabilities } from '../capabilities.js';
5
7
  import { resolveRecord, unwrapSysId } from '../resolve-record.js';
6
- import { detectDocker, defaultRun, installDockerMidServer, validateMidCredentials } from '../midserver-install.js';
8
+ import { detectDocker, defaultRun, installDockerMidServer, midInstallDirectory, validateMidCredentials } from '../midserver-install.js';
7
9
 
8
10
  const AGENT_TABLE = 'ecc_agent';
9
11
  const QUEUE_TABLE = 'ecc_queue';
@@ -190,6 +192,24 @@ async function waitForMidServerUp(app, name, timeoutMs, intervalMs) {
190
192
  throw new Error(`MID Server '${fullName}' did not report Up within ${timeoutMs}ms`);
191
193
  }
192
194
 
195
+ function removeLocalMidServer(instanceURL, name) {
196
+ const installDir = midInstallDirectory(instanceURL, name);
197
+ const composePath = path.join(installDir, 'docker-compose.yaml');
198
+ if (!fs.existsSync(composePath)) return false;
199
+ try {
200
+ defaultRun('docker', ['compose', '-f', composePath, 'down'], { cwd: installDir });
201
+ } catch {
202
+ defaultRun('sudo', ['systemctl', 'start', 'docker']);
203
+ defaultRun('sudo', ['docker', 'compose', '-f', composePath, 'down'], { cwd: installDir });
204
+ }
205
+ try {
206
+ fs.rmSync(installDir, { recursive: true, force: true });
207
+ } catch {
208
+ defaultRun('sudo', ['rm', '-rf', '--', installDir]);
209
+ }
210
+ return true;
211
+ }
212
+
193
213
  export function midserversCmd(wrap) {
194
214
  return {
195
215
  command: 'midservers [subcommand]',
@@ -302,7 +322,14 @@ export function midserversCmd(wrap) {
302
322
  handler: wrap(async (argv, app) => {
303
323
  let record;
304
324
  if (argv.identifier) {
305
- record = await resolveMidServer(app.sdk, argv.identifier);
325
+ try {
326
+ record = await resolveMidServer(app.sdk, argv.identifier);
327
+ } catch (error) {
328
+ if (error.code !== 'not_found') throw error;
329
+ const localDir = midInstallDirectory(app.getEffectiveInstance(), argv.identifier);
330
+ if (!fs.existsSync(path.join(localDir, 'docker-compose.yaml'))) throw error;
331
+ record = null;
332
+ }
306
333
  } else {
307
334
  if (!canPrompt() || app.output.getFormat() !== FormatAuto) {
308
335
  throw new Error('An identifier is required outside an interactive terminal');
@@ -323,11 +350,12 @@ export function midserversCmd(wrap) {
323
350
  });
324
351
  if (!record) return;
325
352
  }
326
- const sysId = unwrapSysId(record);
353
+ const sysId = record ? unwrapSysId(record) : null;
327
354
  const name = getStringField(record, 'name') || argv.identifier || sysId;
328
355
  await confirmDelete(app, argv, `Remove MID Server '${name}'`);
329
- await app.sdk.delete(AGENT_TABLE, sysId);
330
- app.ok({ name, sys_id: sysId, deleted: true }, { summary: `Removed MID Server '${name}'` });
356
+ const localRemoved = removeLocalMidServer(app.getEffectiveInstance(), name);
357
+ if (record) await app.sdk.delete(AGENT_TABLE, sysId);
358
+ app.ok({ name, sys_id: sysId, deleted: Boolean(record), local_compose_removed: localRemoved }, { summary: `Removed MID Server '${name}'${localRemoved ? ' and its local Compose stack' : ''}` });
331
359
  }),
332
360
  })
333
361
  .command({
@@ -90,6 +90,11 @@ function instanceName(instanceURL) {
90
90
  return hostname.replace(/\.service-now\.com$|\.servicenowservices\.com$/i, '').replace(/[^A-Za-z0-9._-]/g, '_');
91
91
  }
92
92
 
93
+ export function midInstallDirectory(instanceURL, name, homeDirectory = os.homedir()) {
94
+ const shortName = normalizeMidName(name);
95
+ return path.join(homeDirectory, '.servicenow', 'mids', instanceName(instanceURL), shortName);
96
+ }
97
+
93
98
  export async function installDockerMidServer({
94
99
  instanceURL,
95
100
  username,
@@ -106,8 +111,7 @@ export async function installDockerMidServer({
106
111
  const displayName = `mid.server.${shortName}`;
107
112
  const releaseName = suppliedReleaseName || (await validateMidCredentials(instanceURL, username, password, fetchImpl)).releaseName;
108
113
  const recipeURL = buildMidRecipeUrl(releaseName);
109
- const serviceNowInstance = instanceName(instanceURL);
110
- const installDir = directory || path.join(os.homedir(), '.servicenow', 'mids', serviceNowInstance, shortName);
114
+ const installDir = directory || midInstallDirectory(instanceURL, name);
111
115
  fs.mkdirSync(installDir, { recursive: true, mode: 0o700 });
112
116
  const exportDir = path.join(installDir, 'export');
113
117
  fs.mkdirSync(exportDir, { recursive: true, mode: 0o700 });