@tenonhq/sincronia-core 0.0.67 → 0.0.69

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/dist/FileUtils.js CHANGED
@@ -37,6 +37,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.writeFileForce = exports.writeSNFileForce = exports.writeSNFileIfNotExists = exports.writeBuildFile = exports.summarizeFile = exports.encodedPathsToFilePaths = exports.isValidPath = exports.splitEncodedPaths = exports.getPathsInPath = exports.isDirectory = exports.toAbsolutePath = exports.getFileContextFromPath = exports.getBuildExt = exports.isUnderPath = exports.appendToPath = exports.pathExists = exports.createDirRecursively = exports.writeSNFileCurry = exports.writeScopeManifest = exports.writeManifestFile = exports.SNFileExists = void 0;
40
+ exports.writeEnvVar = writeEnvVar;
41
+ exports.writeEnvVars = writeEnvVars;
40
42
  const constants_1 = require("./constants");
41
43
  const fs_1 = __importStar(require("fs"));
42
44
  const path_1 = __importDefault(require("path"));
@@ -255,3 +257,61 @@ exports.writeBuildFile = writeBuildFile;
255
257
  exports.writeSNFileIfNotExists = (0, exports.writeSNFileCurry)(true);
256
258
  exports.writeSNFileForce = (0, exports.writeSNFileCurry)(false);
257
259
  exports.writeFileForce = fs_1.promises.writeFile;
260
+ // ============================================================================
261
+ // .env File Utilities — merge-style writes (never destructive)
262
+ // ============================================================================
263
+ function escapeRegex(str) {
264
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
265
+ }
266
+ function quoteEnvValue(value) {
267
+ if (/[\s#"'\\]/.test(value)) {
268
+ return "\"" + value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\n/g, "\\n") + "\"";
269
+ }
270
+ return value;
271
+ }
272
+ function mergeEnvLine(content, key, value) {
273
+ const escaped = escapeRegex(key);
274
+ const regex = new RegExp("^" + escaped + "=.*$", "m");
275
+ const line = key + "=" + quoteEnvValue(value);
276
+ if (regex.test(content)) {
277
+ return content.replace(regex, line);
278
+ }
279
+ if (content.length > 0 && content.charAt(content.length - 1) !== "\n") {
280
+ content += "\n";
281
+ }
282
+ return content + line + "\n";
283
+ }
284
+ function readEnvFile(envPath) {
285
+ try {
286
+ return fs_1.default.readFileSync(envPath, "utf8");
287
+ }
288
+ catch (e) {
289
+ return "";
290
+ }
291
+ }
292
+ /**
293
+ * @description Writes a single env variable to a .env file, preserving existing values.
294
+ * @param {Object} params - Parameters object.
295
+ * @param {string} params.key - The environment variable name.
296
+ * @param {string} params.value - The value to set.
297
+ * @param {string} [params.envPath] - Path to .env file. Defaults to process.cwd()/.env.
298
+ */
299
+ function writeEnvVar(params) {
300
+ const resolvedPath = params.envPath || path_1.default.resolve(process.cwd(), ".env");
301
+ const content = mergeEnvLine(readEnvFile(resolvedPath), params.key, params.value);
302
+ fs_1.default.writeFileSync(resolvedPath, content, "utf8");
303
+ }
304
+ /**
305
+ * @description Writes multiple env variables to a .env file in a single read/write cycle.
306
+ * @param {Object} params - Parameters object.
307
+ * @param {Array<{key: string; value: string}>} params.vars - Array of key/value pairs to write.
308
+ * @param {string} [params.envPath] - Path to .env file. Defaults to process.cwd()/.env.
309
+ */
310
+ function writeEnvVars(params) {
311
+ const resolvedPath = params.envPath || path_1.default.resolve(process.cwd(), ".env");
312
+ let content = readEnvFile(resolvedPath);
313
+ params.vars.forEach(({ key, value }) => {
314
+ content = mergeEnvLine(content, key, value);
315
+ });
316
+ fs_1.default.writeFileSync(resolvedPath, content, "utf8");
317
+ }
package/dist/appUtils.js CHANGED
@@ -105,17 +105,18 @@ const processRecsInManTable = async (tablePath, table, forceWrite) => {
105
105
  }, []);
106
106
  return Promise.all(filePromises);
107
107
  };
108
- const processTablesInManifest = async (tables, forceWrite) => {
108
+ const processTablesInManifest = async (tables, forceWrite, sourcePath) => {
109
+ var basePath = sourcePath || ConfigManager.getSourcePath();
109
110
  const tableNames = Object.keys(tables);
110
111
  const tablePromises = tableNames.map((tableName) => {
111
- return processRecsInManTable(path_1.default.join(ConfigManager.getSourcePath(), tableName), tables[tableName], forceWrite);
112
+ return processRecsInManTable(path_1.default.join(basePath, tableName), tables[tableName], forceWrite);
112
113
  });
113
114
  await Promise.all(tablePromises);
114
115
  };
115
- const processManifest = async (manifest, forceWrite = false) => {
116
+ const processManifest = async (manifest, forceWrite = false, sourcePath) => {
116
117
  const tableCount = Object.keys(manifest.tables).length;
117
118
  FileLogger_1.fileLogger.debug("Processing manifest: " + (manifest.scope || "legacy") + " (" + tableCount + " tables)");
118
- await processTablesInManifest(manifest.tables, forceWrite);
119
+ await processTablesInManifest(manifest.tables, forceWrite, sourcePath);
119
120
  if (manifest.scope) {
120
121
  await fUtils.writeScopeManifest(manifest.scope, manifest);
121
122
  }
@@ -134,11 +135,13 @@ const syncManifest = async (scope) => {
134
135
  Logger_1.logger.info("Refreshing scope: " + scope + "...");
135
136
  const client = (0, snClient_1.defaultClient)();
136
137
  const config = ConfigManager.getConfig();
138
+ // Resolve scope-specific source directory
139
+ var scopeSourcePath = ConfigManager.getSourcePathForScope(scope);
137
140
  const newManifest = await (0, snClient_1.unwrapSNResponse)(client.getManifest(scope, config));
138
141
  const refreshTableCount = Object.keys(newManifest.tables).length;
139
142
  FileLogger_1.fileLogger.debug("Refreshed manifest for " + scope + ": " + refreshTableCount + " tables");
140
143
  await fUtils.writeScopeManifest(scope, newManifest);
141
- await (0, exports.processMissingFiles)(newManifest);
144
+ await (0, exports.processMissingFiles)(newManifest, scopeSourcePath);
142
145
  // Update the in-memory manifest for this scope
143
146
  if (typeof curManifest === "object" && !curManifest.tables) {
144
147
  curManifest[scope] = newManifest;
@@ -229,18 +232,18 @@ const checkTablesForMissing = async (topPath, tables, missingFunc) => {
229
232
  });
230
233
  await Promise.all(recCheckPromises);
231
234
  };
232
- const findMissingFiles = async (manifest) => {
235
+ const findMissingFiles = async (manifest, sourcePath) => {
233
236
  const missing = {};
234
237
  const { tables } = manifest;
235
238
  const missingTableFunc = markFileMissing(missing);
236
- await checkTablesForMissing(ConfigManager.getSourcePath(), tables, missingTableFunc);
239
+ await checkTablesForMissing(sourcePath || ConfigManager.getSourcePath(), tables, missingTableFunc);
237
240
  // missing gets mutated along the way as things get processed
238
241
  return missing;
239
242
  };
240
243
  exports.findMissingFiles = findMissingFiles;
241
- const processMissingFiles = async (newManifest) => {
244
+ const processMissingFiles = async (newManifest, sourcePath) => {
242
245
  try {
243
- const missing = await (0, exports.findMissingFiles)(newManifest);
246
+ const missing = await (0, exports.findMissingFiles)(newManifest, sourcePath);
244
247
  const missingTableCount = Object.keys(missing).length;
245
248
  if (missingTableCount > 0) {
246
249
  FileLogger_1.fileLogger.debug("Downloading missing files from " + missingTableCount + " tables");
@@ -248,7 +251,7 @@ const processMissingFiles = async (newManifest) => {
248
251
  const { tableOptions = {} } = ConfigManager.getConfig();
249
252
  const client = (0, snClient_1.defaultClient)();
250
253
  const filesToProcess = await (0, snClient_1.unwrapSNResponse)(client.getMissingFiles(missing, tableOptions));
251
- await processTablesInManifest(filesToProcess, false);
254
+ await processTablesInManifest(filesToProcess, false, sourcePath);
252
255
  }
253
256
  catch (e) {
254
257
  throw e;
@@ -1,37 +1,4 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
36
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
4
  };
@@ -51,8 +18,7 @@ const inquirer_1 = __importDefault(require("inquirer"));
51
18
  const chalk_1 = __importDefault(require("chalk"));
52
19
  const Logger_1 = require("./Logger");
53
20
  const commands_1 = require("./commands");
54
- const fs = __importStar(require("fs"));
55
- const path = __importStar(require("path"));
21
+ const FileUtils_1 = require("./FileUtils");
56
22
  // --- Token & API Helpers ---
57
23
  function getClickUpToken() {
58
24
  var token = process.env.CLICKUP_API_TOKEN;
@@ -337,7 +303,7 @@ async function clickupSetupCommand(args) {
337
303
  Logger_1.logger.info(" Workspace: " + teams[i].name + " (" + memberCount + " members)");
338
304
  }
339
305
  // Write token to .env file
340
- await writeEnvVar("CLICKUP_API_TOKEN", token);
306
+ (0, FileUtils_1.writeEnvVar)({ key: "CLICKUP_API_TOKEN", value: token });
341
307
  Logger_1.logger.info("");
342
308
  Logger_1.logger.success(chalk_1.default.green("✓ ClickUp configured! Token saved to .env"));
343
309
  Logger_1.logger.info("");
@@ -504,26 +470,3 @@ function refineUpdateSetName(params) {
504
470
  .trim()
505
471
  .substring(0, 80);
506
472
  }
507
- // --- Env File Helper ---
508
- async function writeEnvVar(key, value) {
509
- var envPath = path.resolve(process.cwd(), ".env");
510
- var content = "";
511
- try {
512
- content = fs.readFileSync(envPath, "utf8");
513
- }
514
- catch (e) {
515
- // File doesn't exist yet — will create
516
- }
517
- var regex = new RegExp("^" + key + "=.*$", "m");
518
- var line = key + "=" + value;
519
- if (regex.test(content)) {
520
- content = content.replace(regex, line);
521
- }
522
- else {
523
- if (content.length > 0 && content.charAt(content.length - 1) !== "\n") {
524
- content = content + "\n";
525
- }
526
- content = content + line + "\n";
527
- }
528
- fs.writeFileSync(envPath, content, "utf8");
529
- }
package/dist/commander.js CHANGED
@@ -13,6 +13,7 @@ const claudeCommand_1 = require("./claudeCommand");
13
13
  const createRecordCommand_1 = require("./createRecordCommand");
14
14
  const deleteRecordCommand_1 = require("./deleteRecordCommand");
15
15
  const clickupCommands_1 = require("./clickupCommands");
16
+ const loginCommand_1 = require("./loginCommand");
16
17
  const yargs_1 = __importDefault(require("yargs"));
17
18
  async function initCommands() {
18
19
  const sharedOptions = {
@@ -74,7 +75,34 @@ async function initCommands() {
74
75
  .command("download <scope>", "Downloads a scoped application's files from ServiceNow. Must specify a scope prefix for a scoped app.", sharedOptions, async (args) => {
75
76
  await (0, commands_1.downloadCommand)(args);
76
77
  })
77
- .command("init", "Provisions an initial project for you", sharedOptions, commands_1.initCommand)
78
+ .command("init", "Set up Sincronia discovers installed packages, authenticates, and configures your project", sharedOptions, commands_1.initCommand)
79
+ .command("login [plugin]", "Authenticate with ServiceNow and other integrations", function (cmdArgs) {
80
+ cmdArgs.positional("plugin", {
81
+ describe: "Plugin name to login (e.g., clickup). Omit for ServiceNow only.",
82
+ type: "string",
83
+ });
84
+ cmdArgs.options({
85
+ ...sharedOptions,
86
+ all: {
87
+ type: "boolean",
88
+ default: false,
89
+ describe: "Login to all detected integrations",
90
+ },
91
+ instance: {
92
+ type: "string",
93
+ describe: "ServiceNow instance URL (non-interactive)",
94
+ },
95
+ user: {
96
+ type: "string",
97
+ describe: "ServiceNow username (non-interactive)",
98
+ },
99
+ password: {
100
+ type: "string",
101
+ describe: "ServiceNow password (non-interactive)",
102
+ },
103
+ });
104
+ return cmdArgs;
105
+ }, loginCommand_1.loginCommand)
78
106
  .command("initScopes", "Provisions an initial project for the scopes defined in the config", {
79
107
  ...sharedOptions,
80
108
  delay: {
package/dist/commands.js CHANGED
@@ -48,7 +48,7 @@ exports.statusCommand = statusCommand;
48
48
  const ConfigManager = __importStar(require("./config"));
49
49
  const Watcher_1 = require("./Watcher");
50
50
  const AppUtils = __importStar(require("./appUtils"));
51
- const wizard_1 = require("./wizard");
51
+ const orchestrator_1 = require("./initSystem/orchestrator");
52
52
  const Logger_1 = require("./Logger");
53
53
  const FileLogger_1 = require("./FileLogger");
54
54
  const logMessages_1 = require("./logMessages");
@@ -244,7 +244,7 @@ async function downloadCommand(args) {
244
244
  async function initCommand(args) {
245
245
  setLogLevel(args);
246
246
  try {
247
- await (0, wizard_1.startWizard)();
247
+ await (0, orchestrator_1.runInit)({ logLevel: args.logLevel });
248
248
  }
249
249
  catch (e) {
250
250
  throw e;
package/dist/config.js CHANGED
@@ -45,6 +45,7 @@ exports.getManifest = getManifest;
45
45
  exports.getManifestPath = getManifestPath;
46
46
  exports.getScopeManifestPath = getScopeManifestPath;
47
47
  exports.getSourcePath = getSourcePath;
48
+ exports.getSourcePathForScope = getSourcePathForScope;
48
49
  exports.getBuildPath = getBuildPath;
49
50
  exports.getEnvPath = getEnvPath;
50
51
  exports.getDiffPath = getDiffPath;
@@ -151,6 +152,14 @@ function getSourcePath() {
151
152
  return source_path;
152
153
  throw new Error("Error getting source path");
153
154
  }
155
+ function getSourcePathForScope(scopeName) {
156
+ var cfg = getConfig();
157
+ var scopeConfig = cfg.scopes && cfg.scopes[scopeName];
158
+ if (scopeConfig && typeof scopeConfig === "object" && scopeConfig.sourceDirectory) {
159
+ return path_1.default.resolve(getRootDir(), scopeConfig.sourceDirectory);
160
+ }
161
+ return getSourcePath();
162
+ }
154
163
  function getBuildPath() {
155
164
  if (build_path)
156
165
  return build_path;
@@ -0,0 +1,210 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.corePlugin = void 0;
40
+ exports.validateCoreLogin = validateCoreLogin;
41
+ exports.normalizeInstance = normalizeInstance;
42
+ const snClient_1 = require("../snClient");
43
+ const Logger_1 = require("../Logger");
44
+ const ConfigManager = __importStar(require("../config"));
45
+ const AppUtils = __importStar(require("../appUtils"));
46
+ const inquirer_1 = __importDefault(require("inquirer"));
47
+ const chalk_1 = __importDefault(require("chalk"));
48
+ const fs_1 = __importDefault(require("fs"));
49
+ const path_1 = __importDefault(require("path"));
50
+ /**
51
+ * @description Core init plugin — handles ServiceNow authentication, app selection, and file download.
52
+ * This plugin is always included in sinc init and sinc login.
53
+ */
54
+ exports.corePlugin = {
55
+ name: "core",
56
+ displayName: "ServiceNow",
57
+ description: "Connect to a ServiceNow instance and sync application files",
58
+ login: [
59
+ {
60
+ envKey: "SN_INSTANCE",
61
+ prompt: {
62
+ type: "input",
63
+ message: "ServiceNow instance (e.g. mycompany.service-now.com):",
64
+ },
65
+ required: true,
66
+ },
67
+ {
68
+ envKey: "SN_USER",
69
+ prompt: {
70
+ type: "input",
71
+ message: "Username:",
72
+ },
73
+ required: true,
74
+ },
75
+ {
76
+ envKey: "SN_PASSWORD",
77
+ prompt: {
78
+ type: "password",
79
+ message: "Password:",
80
+ mask: "*",
81
+ },
82
+ required: true,
83
+ },
84
+ ],
85
+ configure: [
86
+ {
87
+ key: "app",
88
+ label: "Selecting ServiceNow application",
89
+ run: async (context) => {
90
+ const instanceUrl = normalizeInstance(context.env.SN_INSTANCE);
91
+ const client = (0, snClient_1.snClient)(instanceUrl, context.env.SN_USER, context.env.SN_PASSWORD);
92
+ Logger_1.logger.info("Fetching application list...");
93
+ const apps = await (0, snClient_1.unwrapSNResponse)(client.getAppList());
94
+ if (apps.length === 0) {
95
+ Logger_1.logger.warn("No applications found on this instance.");
96
+ return null;
97
+ }
98
+ const choices = apps.map((app) => ({
99
+ name: app.displayName + " (" + app.scope + ")",
100
+ value: app.scope,
101
+ short: app.displayName,
102
+ }));
103
+ const answer = await inquirer_1.default.prompt([{
104
+ type: "list",
105
+ name: "app",
106
+ message: "Which app would you like to work with?",
107
+ choices,
108
+ }]);
109
+ context.answers.selectedScope = answer.app;
110
+ context.answers.apps = apps;
111
+ return answer.app;
112
+ },
113
+ },
114
+ ],
115
+ initialize: async (context) => {
116
+ const scope = context.answers.selectedScope;
117
+ if (!scope) {
118
+ Logger_1.logger.warn("No application selected — skipping initialization.");
119
+ return;
120
+ }
121
+ const rootDir = context.rootDir;
122
+ const configPath = path_1.default.join(rootDir, "sinc.config.js");
123
+ // Write or merge sinc.config.js
124
+ let hasExistingConfig = false;
125
+ try {
126
+ fs_1.default.accessSync(configPath, fs_1.default.constants.F_OK);
127
+ hasExistingConfig = true;
128
+ }
129
+ catch (e) {
130
+ // No existing config
131
+ }
132
+ if (!hasExistingConfig) {
133
+ Logger_1.logger.info("Generating sinc.config.js...");
134
+ fs_1.default.writeFileSync(configPath, ConfigManager.getDefaultConfigFile(), "utf8");
135
+ }
136
+ else {
137
+ Logger_1.logger.info("sinc.config.js already exists — preserving configuration.");
138
+ }
139
+ // Reload configs so ConfigManager picks up the new/existing config
140
+ try {
141
+ await ConfigManager.loadConfigs();
142
+ }
143
+ catch (e) {
144
+ Logger_1.logger.warn("Config reload incomplete — this is expected during first-time init.");
145
+ }
146
+ // Check if manifest already exists for this scope
147
+ const manifestPath = path_1.default.join(rootDir, "sinc.manifest." + scope + ".json");
148
+ let hasManifest = false;
149
+ try {
150
+ fs_1.default.accessSync(manifestPath, fs_1.default.constants.F_OK);
151
+ hasManifest = true;
152
+ }
153
+ catch (e) {
154
+ // No existing manifest
155
+ }
156
+ if (hasManifest) {
157
+ const redownload = await inquirer_1.default.prompt([{
158
+ type: "confirm",
159
+ name: "confirmed",
160
+ message: "Manifest for " + scope + " already exists. Re-download?",
161
+ default: false,
162
+ }]);
163
+ if (!redownload.confirmed) {
164
+ Logger_1.logger.info("Skipping download for " + scope);
165
+ return;
166
+ }
167
+ }
168
+ // Download application files — errors propagate to orchestrator
169
+ Logger_1.logger.info("Downloading " + scope + "...");
170
+ const instanceUrl = normalizeInstance(context.env.SN_INSTANCE);
171
+ const client = (0, snClient_1.snClient)(instanceUrl, context.env.SN_USER, context.env.SN_PASSWORD);
172
+ const config = ConfigManager.getConfig();
173
+ const man = await (0, snClient_1.unwrapSNResponse)(client.getManifest(scope, config, true));
174
+ await AppUtils.processManifest(man);
175
+ const tableNames = Object.keys(man.tables || {});
176
+ const recordCount = tableNames.reduce((sum, t) => {
177
+ return sum + Object.keys(man.tables[t].records || {}).length;
178
+ }, 0);
179
+ Logger_1.logger.success(chalk_1.default.green("✓ ServiceNow configured — " + tableNames.length + " tables, " + recordCount + " records"));
180
+ },
181
+ };
182
+ /**
183
+ * @description Validates ServiceNow credentials by testing the connection.
184
+ * Called by the orchestrator after all core login hooks are collected.
185
+ */
186
+ async function validateCoreLogin(context) {
187
+ const instance = context.env.SN_INSTANCE;
188
+ const user = context.env.SN_USER;
189
+ const password = context.env.SN_PASSWORD;
190
+ if (!instance || !user || !password) {
191
+ return "Missing required credentials";
192
+ }
193
+ const instanceUrl = normalizeInstance(instance);
194
+ try {
195
+ const client = (0, snClient_1.snClient)(instanceUrl, user, password);
196
+ await (0, snClient_1.unwrapSNResponse)(client.getAppList());
197
+ context.env.SN_INSTANCE = instanceUrl;
198
+ return true;
199
+ }
200
+ catch (e) {
201
+ return "Connection failed — check your instance URL, username, and password.";
202
+ }
203
+ }
204
+ function normalizeInstance(instance) {
205
+ let url = instance.trim().replace("https://", "").replace("http://", "");
206
+ if (!url.endsWith("/")) {
207
+ url += "/";
208
+ }
209
+ return url;
210
+ }
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.discoverPlugins = discoverPlugins;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const Logger_1 = require("../Logger");
40
+ const SKIP_PACKAGES = new Set(["sincronia-core", "sincronia-types"]);
41
+ const MAX_PARENT_DEPTH = 3;
42
+ /**
43
+ * @description Scans node_modules for @tenonhq/sincronia-* packages that export a sincPlugin.
44
+ * @returns {Sinc.InitPlugin[]} Array of discovered init plugins.
45
+ */
46
+ function discoverPlugins() {
47
+ const plugins = [];
48
+ const seen = new Set();
49
+ const searchPaths = [
50
+ path.resolve(process.cwd(), "node_modules", "@tenonhq"),
51
+ path.resolve(__dirname, "..", "..", "..", "@tenonhq"),
52
+ ];
53
+ // Check parent directories for monorepo hoisted node_modules (capped depth)
54
+ let current = process.cwd();
55
+ for (let depth = 0; depth < MAX_PARENT_DEPTH; depth++) {
56
+ const parent = path.dirname(current);
57
+ if (parent === current)
58
+ break;
59
+ const hoisted = path.join(parent, "node_modules", "@tenonhq");
60
+ if (!searchPaths.includes(hoisted)) {
61
+ searchPaths.push(hoisted);
62
+ }
63
+ current = parent;
64
+ }
65
+ for (const searchPath of searchPaths) {
66
+ let dirs;
67
+ try {
68
+ dirs = fs.readdirSync(searchPath);
69
+ }
70
+ catch (e) {
71
+ continue;
72
+ }
73
+ dirs
74
+ .filter(name => name.startsWith("sincronia-") && !SKIP_PACKAGES.has(name) && !seen.has(name))
75
+ .forEach(dirName => {
76
+ seen.add(dirName);
77
+ try {
78
+ const pkg = require("@tenonhq/" + dirName);
79
+ if (pkg && pkg.sincPlugin && pkg.sincPlugin.name && pkg.sincPlugin.displayName) {
80
+ plugins.push(pkg.sincPlugin);
81
+ Logger_1.logger.debug("Discovered init plugin: " + pkg.sincPlugin.displayName + " (" + dirName + ")");
82
+ }
83
+ }
84
+ catch (e) {
85
+ const message = e instanceof Error ? e.message : String(e);
86
+ Logger_1.logger.warn("Failed to load plugin " + dirName + ": " + message);
87
+ }
88
+ });
89
+ }
90
+ return plugins;
91
+ }