@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 +60 -0
- package/dist/appUtils.js +13 -10
- package/dist/clickupCommands.js +2 -59
- package/dist/commander.js +29 -1
- package/dist/commands.js +2 -2
- package/dist/config.js +9 -0
- package/dist/initSystem/corePlugin.js +210 -0
- package/dist/initSystem/discovery.js +91 -0
- package/dist/initSystem/orchestrator.js +312 -0
- package/dist/loginCommand.js +20 -0
- package/dist/tests/clickupCommands.test.js +9 -1
- package/dist/tests/discovery.test.js +56 -0
- package/dist/tests/orchestrator.test.js +23 -0
- package/dist/tests/writeEnvVar.test.js +119 -0
- package/dist/wizard.js +8 -146
- package/package.json +2 -4
- package/LICENSE +0 -674
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(
|
|
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;
|
package/dist/clickupCommands.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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", "
|
|
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
|
|
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,
|
|
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
|
+
}
|