@tenonhq/sincronia-core 0.0.68 → 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
+ }
@@ -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;
@@ -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
+ }
@@ -0,0 +1,312 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.runInit = runInit;
7
+ exports.runLogin = runLogin;
8
+ const inquirer_1 = __importDefault(require("inquirer"));
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ const dotenv_1 = __importDefault(require("dotenv"));
11
+ const fs_1 = __importDefault(require("fs"));
12
+ const path_1 = __importDefault(require("path"));
13
+ const Logger_1 = require("../Logger");
14
+ const FileUtils_1 = require("../FileUtils");
15
+ const discovery_1 = require("./discovery");
16
+ const corePlugin_1 = require("./corePlugin");
17
+ // ============================================================================
18
+ // Init Context
19
+ // ============================================================================
20
+ /**
21
+ * @description Builds an InitContext from the current environment.
22
+ * Reads existing .env if present. Pulls process.env fallbacks only for
23
+ * env keys declared by the provided plugins (no hardcoded allowlist).
24
+ */
25
+ function buildInitContext(plugins) {
26
+ const rootDir = process.cwd();
27
+ const envPath = path_1.default.resolve(rootDir, ".env");
28
+ const env = {};
29
+ // Load existing .env values
30
+ try {
31
+ const parsed = dotenv_1.default.parse(fs_1.default.readFileSync(envPath, "utf8"));
32
+ Object.assign(env, parsed);
33
+ }
34
+ catch (e) {
35
+ // No .env yet — starting fresh
36
+ }
37
+ // Pull from process.env for keys declared by plugins but missing from .env
38
+ const pluginEnvKeys = plugins.flatMap(p => [
39
+ ...(p.login || []).map(h => h.envKey),
40
+ ...(p.configure || []).map(h => h.key),
41
+ ]);
42
+ pluginEnvKeys.forEach(key => {
43
+ if (process.env[key] && !env[key]) {
44
+ env[key] = process.env[key];
45
+ }
46
+ });
47
+ let hasConfig = false;
48
+ try {
49
+ fs_1.default.accessSync(path_1.default.join(rootDir, "sinc.config.js"), fs_1.default.constants.F_OK);
50
+ hasConfig = true;
51
+ }
52
+ catch (e) {
53
+ // No config yet
54
+ }
55
+ return { env, answers: {}, rootDir, hasConfig, inquirer: inquirer_1.default, chalk: chalk_1.default };
56
+ }
57
+ // ============================================================================
58
+ // Plugin Selection
59
+ // ============================================================================
60
+ async function promptPluginSelection(externalPlugins) {
61
+ if (externalPlugins.length === 0) {
62
+ return [corePlugin_1.corePlugin];
63
+ }
64
+ const choices = externalPlugins.map(plugin => ({
65
+ name: plugin.displayName + " — " + plugin.description,
66
+ value: plugin.name,
67
+ checked: false,
68
+ }));
69
+ const answer = await inquirer_1.default.prompt([{
70
+ type: "checkbox",
71
+ name: "plugins",
72
+ message: "Which integrations would you like to configure?",
73
+ choices,
74
+ }]);
75
+ const selectedNames = new Set(answer.plugins);
76
+ const selected = externalPlugins.filter(p => selectedNames.has(p.name));
77
+ return [corePlugin_1.corePlugin, ...selected];
78
+ }
79
+ async function runLoginPhase(plugin, context) {
80
+ const hooks = plugin.login;
81
+ if (!hooks || hooks.length === 0)
82
+ return;
83
+ for (const hook of hooks) {
84
+ const existingValue = context.env[hook.envKey] || "";
85
+ // Show instructions if provided
86
+ if (hook.instructions && hook.instructions.length > 0) {
87
+ Logger_1.logger.info("");
88
+ hook.instructions.forEach(line => Logger_1.logger.info(line));
89
+ Logger_1.logger.info("");
90
+ }
91
+ // Build the prompt
92
+ const promptConfig = {
93
+ type: hook.prompt.type,
94
+ name: "value",
95
+ message: hook.prompt.message,
96
+ };
97
+ if (hook.prompt.mask) {
98
+ promptConfig.mask = hook.prompt.mask;
99
+ }
100
+ // Show existing value as default for non-password fields
101
+ if (existingValue && hook.prompt.type !== "password") {
102
+ promptConfig.default = existingValue;
103
+ }
104
+ // Add basic validation for required fields
105
+ if (hook.required !== false) {
106
+ promptConfig.validate = (input) => {
107
+ if (!input || input.trim() === "")
108
+ return "This field is required";
109
+ return true;
110
+ };
111
+ }
112
+ const answer = await inquirer_1.default.prompt([promptConfig]);
113
+ context.env[hook.envKey] = answer.value.trim();
114
+ }
115
+ // Run per-hook validation if defined
116
+ for (const hook of hooks) {
117
+ if (hook.validate) {
118
+ const result = await hook.validate(context.env[hook.envKey], context);
119
+ if (result !== true) {
120
+ Logger_1.logger.error(chalk_1.default.red("✗ " + result));
121
+ throw new Error("Validation failed for " + hook.envKey);
122
+ }
123
+ }
124
+ }
125
+ // Core plugin gets special post-login validation (all 3 credentials at once)
126
+ if (plugin.name === "core") {
127
+ Logger_1.logger.info("Validating credentials...");
128
+ const coreResult = await (0, corePlugin_1.validateCoreLogin)(context);
129
+ if (coreResult !== true) {
130
+ Logger_1.logger.error(chalk_1.default.red("✗ " + coreResult));
131
+ throw new Error("ServiceNow login failed");
132
+ }
133
+ Logger_1.logger.success(chalk_1.default.green("✓ Connected to " + context.env.SN_INSTANCE));
134
+ }
135
+ }
136
+ async function runConfigurePhase(plugin, context) {
137
+ const hooks = plugin.configure;
138
+ if (!hooks || hooks.length === 0)
139
+ return;
140
+ for (const hook of hooks) {
141
+ Logger_1.logger.debug("Running configure hook: " + hook.label);
142
+ const result = await hook.run(context);
143
+ if (result !== null && result !== undefined) {
144
+ context.answers[hook.key] = result;
145
+ }
146
+ }
147
+ }
148
+ /**
149
+ * @description Saves env vars declared by plugins to .env (merge-style).
150
+ * Only writes keys that plugins explicitly declared via login hooks or
151
+ * configure hooks — never writes transient context values.
152
+ */
153
+ function saveEnvVars(context, plugins) {
154
+ const pluginKeys = new Set();
155
+ plugins.forEach(plugin => {
156
+ (plugin.login || []).forEach(hook => pluginKeys.add(hook.envKey));
157
+ (plugin.configure || []).forEach(hook => {
158
+ if (context.env[hook.key])
159
+ pluginKeys.add(hook.key);
160
+ });
161
+ });
162
+ const vars = Array.from(pluginKeys)
163
+ .filter(key => context.env[key])
164
+ .map(key => ({ key, value: context.env[key] }));
165
+ if (vars.length > 0) {
166
+ (0, FileUtils_1.writeEnvVars)({ vars });
167
+ // Also set on process.env for immediate use
168
+ vars.forEach(({ key, value }) => { process.env[key] = value; });
169
+ Logger_1.logger.success(chalk_1.default.green("✓ Saved to .env (existing values preserved)"));
170
+ }
171
+ }
172
+ async function runInit(options) {
173
+ let failed = false;
174
+ try {
175
+ Logger_1.logger.info("");
176
+ Logger_1.logger.info(chalk_1.default.bold(" Sincronia Setup"));
177
+ Logger_1.logger.info(" " + "═".repeat(40));
178
+ Logger_1.logger.info("");
179
+ // 1. Discover plugins
180
+ const externalPlugins = (0, discovery_1.discoverPlugins)();
181
+ if (externalPlugins.length > 0) {
182
+ Logger_1.logger.info(" Detected packages:");
183
+ Logger_1.logger.info(" ● sincronia-core (" + chalk_1.default.cyan("ServiceNow") + ")");
184
+ externalPlugins.forEach(p => {
185
+ Logger_1.logger.info(" ● sincronia-" + p.name + " (" + chalk_1.default.cyan(p.displayName) + ")");
186
+ });
187
+ Logger_1.logger.info("");
188
+ }
189
+ // 2. Select plugins
190
+ const selectedPlugins = await promptPluginSelection(externalPlugins);
191
+ // 3. Build context (passes plugins so env fallback uses their declared keys)
192
+ const context = buildInitContext(selectedPlugins);
193
+ // 4. Login phase
194
+ Logger_1.logger.info("");
195
+ Logger_1.logger.info(chalk_1.default.bold(" ── Login " + "─".repeat(30)));
196
+ Logger_1.logger.info("");
197
+ for (const plugin of selectedPlugins) {
198
+ await runLoginPhase(plugin, context);
199
+ }
200
+ // 5. Save env vars after login
201
+ saveEnvVars(context, selectedPlugins);
202
+ // 6. Configure phase
203
+ Logger_1.logger.info("");
204
+ Logger_1.logger.info(chalk_1.default.bold(" ── Configure " + "─".repeat(26)));
205
+ Logger_1.logger.info("");
206
+ for (const plugin of selectedPlugins) {
207
+ await runConfigurePhase(plugin, context);
208
+ }
209
+ // 7. Initialize phase
210
+ Logger_1.logger.info("");
211
+ Logger_1.logger.info(chalk_1.default.bold(" ── Initialize " + "─".repeat(25)));
212
+ Logger_1.logger.info("");
213
+ for (const plugin of selectedPlugins) {
214
+ if (plugin.initialize) {
215
+ try {
216
+ await plugin.initialize(context);
217
+ }
218
+ catch (e) {
219
+ const msg = e instanceof Error ? e.message : String(e);
220
+ Logger_1.logger.error("Initialization failed for " + plugin.displayName + ": " + msg);
221
+ failed = true;
222
+ }
223
+ }
224
+ }
225
+ // 8. Summary
226
+ Logger_1.logger.info("");
227
+ Logger_1.logger.info(" " + "═".repeat(40));
228
+ if (failed) {
229
+ Logger_1.logger.warn(" Setup completed with errors. Review the output above.");
230
+ }
231
+ else {
232
+ Logger_1.logger.success(chalk_1.default.green(" Setup complete!") + " Run " + chalk_1.default.cyan("sinc watch") + " to start.");
233
+ }
234
+ Logger_1.logger.info("");
235
+ }
236
+ catch (e) {
237
+ const message = e instanceof Error ? e.message : String(e);
238
+ Logger_1.logger.error("Init failed: " + message);
239
+ }
240
+ }
241
+ async function runLogin(options) {
242
+ try {
243
+ const opts = options || {};
244
+ // Determine which plugins to log in to
245
+ let pluginsToLogin;
246
+ const coreAliases = new Set(["core", "servicenow", "sn"]);
247
+ if (opts.pluginName) {
248
+ if (coreAliases.has(opts.pluginName)) {
249
+ pluginsToLogin = [corePlugin_1.corePlugin];
250
+ }
251
+ else {
252
+ const externalPlugins = (0, discovery_1.discoverPlugins)();
253
+ const match = externalPlugins.find(p => p.name === opts.pluginName);
254
+ if (!match) {
255
+ Logger_1.logger.error("Plugin '" + opts.pluginName + "' not found. Available plugins:");
256
+ externalPlugins.forEach(p => Logger_1.logger.info(" - " + p.name + " (" + p.displayName + ")"));
257
+ return;
258
+ }
259
+ pluginsToLogin = [match];
260
+ }
261
+ }
262
+ else if (opts.all) {
263
+ pluginsToLogin = [corePlugin_1.corePlugin, ...(0, discovery_1.discoverPlugins)()];
264
+ }
265
+ else {
266
+ pluginsToLogin = [corePlugin_1.corePlugin];
267
+ }
268
+ // Build context with discovered plugins for env key resolution
269
+ const context = buildInitContext(pluginsToLogin);
270
+ // Apply CLI flag overrides for non-interactive mode
271
+ if (opts.instance)
272
+ context.env.SN_INSTANCE = opts.instance;
273
+ if (opts.user)
274
+ context.env.SN_USER = opts.user;
275
+ if (opts.password)
276
+ context.env.SN_PASSWORD = opts.password;
277
+ // Dynamic header based on which plugins are being logged in
278
+ const pluginNames = pluginsToLogin.map(p => p.displayName).join(" + ");
279
+ Logger_1.logger.info("");
280
+ Logger_1.logger.info(chalk_1.default.bold(" " + pluginNames + " Login"));
281
+ Logger_1.logger.info(" " + "─".repeat(40));
282
+ Logger_1.logger.info("");
283
+ // Check if all core credentials provided via flags (non-interactive mode)
284
+ const hasAllCoreFlags = opts.instance && opts.user && opts.password;
285
+ if (hasAllCoreFlags && pluginsToLogin.length === 1 && pluginsToLogin[0].name === "core") {
286
+ Logger_1.logger.info("Validating credentials...");
287
+ const result = await (0, corePlugin_1.validateCoreLogin)(context);
288
+ if (result !== true) {
289
+ Logger_1.logger.error(chalk_1.default.red("✗ " + result));
290
+ return;
291
+ }
292
+ Logger_1.logger.success(chalk_1.default.green("✓ Connected to " + context.env.SN_INSTANCE));
293
+ }
294
+ else {
295
+ for (const plugin of pluginsToLogin) {
296
+ await runLoginPhase(plugin, context);
297
+ }
298
+ }
299
+ // Save env vars
300
+ saveEnvVars(context, pluginsToLogin);
301
+ Logger_1.logger.info("");
302
+ Logger_1.logger.info("You can now use:");
303
+ Logger_1.logger.info(" sinc init — Initialize a new project");
304
+ Logger_1.logger.info(" sinc watch — Watch for changes");
305
+ Logger_1.logger.info(" sinc status — Check instance connection");
306
+ Logger_1.logger.info("");
307
+ }
308
+ catch (e) {
309
+ const message = e instanceof Error ? e.message : String(e);
310
+ Logger_1.logger.error("Login failed: " + message);
311
+ }
312
+ }
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.loginCommand = loginCommand;
4
+ const commands_1 = require("./commands");
5
+ const orchestrator_1 = require("./initSystem/orchestrator");
6
+ /**
7
+ * @description Command handler for `sinc login`.
8
+ * Authenticates with ServiceNow and other integrations, saves credentials to .env.
9
+ */
10
+ async function loginCommand(args) {
11
+ (0, commands_1.setLogLevel)(args);
12
+ await (0, orchestrator_1.runLogin)({
13
+ logLevel: args.logLevel,
14
+ pluginName: args.plugin || undefined,
15
+ all: args.all || false,
16
+ instance: args.instance || undefined,
17
+ user: args.user || undefined,
18
+ password: args.password || undefined,
19
+ });
20
+ }
@@ -71,7 +71,15 @@ jest.mock("chalk", function () {
71
71
  bold: identity,
72
72
  };
73
73
  });
74
- jest.mock("fs");
74
+ jest.mock("fs", function () {
75
+ var actual = jest.requireActual("fs");
76
+ return Object.assign({}, actual, {
77
+ readFileSync: jest.fn(),
78
+ writeFileSync: jest.fn(),
79
+ existsSync: jest.fn(),
80
+ promises: actual.promises,
81
+ });
82
+ });
75
83
  jest.mock("child_process");
76
84
  // --- Imports (after mocks) ---
77
85
  const clickupCommands_1 = require("../clickupCommands");
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ // --- Mock setup (must be before imports) ---
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ var mockReaddirSync = jest.fn();
5
+ jest.mock("fs", function () {
6
+ var actual = jest.requireActual("fs");
7
+ return Object.assign({}, actual, {
8
+ readdirSync: mockReaddirSync,
9
+ });
10
+ });
11
+ jest.mock("../Logger", function () {
12
+ return {
13
+ logger: {
14
+ info: jest.fn(),
15
+ error: jest.fn(),
16
+ warn: jest.fn(),
17
+ debug: jest.fn(),
18
+ success: jest.fn(),
19
+ },
20
+ };
21
+ });
22
+ const discovery_1 = require("../initSystem/discovery");
23
+ const Logger_1 = require("../Logger");
24
+ describe("discoverPlugins", function () {
25
+ beforeEach(function () {
26
+ mockReaddirSync.mockReset();
27
+ // Default: no directories found
28
+ mockReaddirSync.mockImplementation(function () {
29
+ throw new Error("ENOENT");
30
+ });
31
+ });
32
+ it("returns empty array when no node_modules found", function () {
33
+ const plugins = (0, discovery_1.discoverPlugins)();
34
+ expect(plugins).toEqual([]);
35
+ });
36
+ it("skips sincronia-core and sincronia-types", function () {
37
+ mockReaddirSync.mockReturnValue([
38
+ "sincronia-core",
39
+ "sincronia-types",
40
+ ]);
41
+ const plugins = (0, discovery_1.discoverPlugins)();
42
+ expect(plugins).toEqual([]);
43
+ });
44
+ it("warns on plugin load failure", function () {
45
+ mockReaddirSync.mockReturnValue(["sincronia-nonexistent"]);
46
+ (0, discovery_1.discoverPlugins)();
47
+ expect(Logger_1.logger.warn).toHaveBeenCalled();
48
+ });
49
+ it("skips packages without sincPlugin export", function () {
50
+ mockReaddirSync.mockReturnValue(["sincronia-sass-plugin"]);
51
+ // sass-plugin exists but doesn't export sincPlugin — it just won't be added
52
+ const plugins = (0, discovery_1.discoverPlugins)();
53
+ const names = plugins.map(function (p) { return p.name; });
54
+ expect(names).not.toContain("sass-plugin");
55
+ });
56
+ });
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const corePlugin_1 = require("../initSystem/corePlugin");
4
+ describe("normalizeInstance", function () {
5
+ it("strips https:// prefix", function () {
6
+ expect((0, corePlugin_1.normalizeInstance)("https://mycompany.service-now.com")).toBe("mycompany.service-now.com/");
7
+ });
8
+ it("strips http:// prefix", function () {
9
+ expect((0, corePlugin_1.normalizeInstance)("http://mycompany.service-now.com")).toBe("mycompany.service-now.com/");
10
+ });
11
+ it("adds trailing slash if missing", function () {
12
+ expect((0, corePlugin_1.normalizeInstance)("mycompany.service-now.com")).toBe("mycompany.service-now.com/");
13
+ });
14
+ it("preserves trailing slash if present", function () {
15
+ expect((0, corePlugin_1.normalizeInstance)("mycompany.service-now.com/")).toBe("mycompany.service-now.com/");
16
+ });
17
+ it("trims whitespace", function () {
18
+ expect((0, corePlugin_1.normalizeInstance)(" mycompany.service-now.com ")).toBe("mycompany.service-now.com/");
19
+ });
20
+ it("handles full URL with protocol and trailing slash", function () {
21
+ expect((0, corePlugin_1.normalizeInstance)("https://mycompany.service-now.com/")).toBe("mycompany.service-now.com/");
22
+ });
23
+ });
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const fs_1 = __importDefault(require("fs"));
7
+ const path_1 = __importDefault(require("path"));
8
+ const os_1 = __importDefault(require("os"));
9
+ const FileUtils_1 = require("../FileUtils");
10
+ describe("writeEnvVar", function () {
11
+ let tmpDir;
12
+ let envPath;
13
+ beforeEach(function () {
14
+ tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), "sinc-test-"));
15
+ envPath = path_1.default.join(tmpDir, ".env");
16
+ });
17
+ afterEach(function () {
18
+ try {
19
+ fs_1.default.rmSync(tmpDir, { recursive: true });
20
+ }
21
+ catch (e) {
22
+ // cleanup best-effort
23
+ }
24
+ });
25
+ it("creates a new .env file from scratch", function () {
26
+ (0, FileUtils_1.writeEnvVar)({ key: "FOO", value: "bar", envPath });
27
+ const content = fs_1.default.readFileSync(envPath, "utf8");
28
+ expect(content).toBe("FOO=bar\n");
29
+ });
30
+ it("replaces an existing key", function () {
31
+ fs_1.default.writeFileSync(envPath, "FOO=old\nBAR=keep\n", "utf8");
32
+ (0, FileUtils_1.writeEnvVar)({ key: "FOO", value: "new", envPath });
33
+ const content = fs_1.default.readFileSync(envPath, "utf8");
34
+ expect(content).toContain("FOO=new");
35
+ expect(content).toContain("BAR=keep");
36
+ expect(content).not.toContain("FOO=old");
37
+ });
38
+ it("appends a new key without clobbering existing ones", function () {
39
+ fs_1.default.writeFileSync(envPath, "EXISTING=value\n", "utf8");
40
+ (0, FileUtils_1.writeEnvVar)({ key: "NEW_KEY", value: "new_value", envPath });
41
+ const content = fs_1.default.readFileSync(envPath, "utf8");
42
+ expect(content).toContain("EXISTING=value");
43
+ expect(content).toContain("NEW_KEY=new_value");
44
+ });
45
+ it("handles file with no trailing newline", function () {
46
+ fs_1.default.writeFileSync(envPath, "A=1", "utf8");
47
+ (0, FileUtils_1.writeEnvVar)({ key: "B", value: "2", envPath });
48
+ const content = fs_1.default.readFileSync(envPath, "utf8");
49
+ expect(content).toContain("A=1");
50
+ expect(content).toContain("B=2");
51
+ // Should not produce "A=1B=2" on one line
52
+ const lines = content.trim().split("\n");
53
+ expect(lines.length).toBe(2);
54
+ });
55
+ it("handles keys with regex metacharacters", function () {
56
+ fs_1.default.writeFileSync(envPath, "MY.KEY[0]=old\n", "utf8");
57
+ (0, FileUtils_1.writeEnvVar)({ key: "MY.KEY[0]", value: "new", envPath });
58
+ const content = fs_1.default.readFileSync(envPath, "utf8");
59
+ expect(content).toContain("MY.KEY[0]=new");
60
+ expect(content).not.toContain("MY.KEY[0]=old");
61
+ // Should not have duplicates
62
+ const matches = content.match(/MY\.KEY\[0\]/g);
63
+ expect(matches).toHaveLength(1);
64
+ });
65
+ });
66
+ describe("writeEnvVars", function () {
67
+ let tmpDir;
68
+ let envPath;
69
+ beforeEach(function () {
70
+ tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), "sinc-test-"));
71
+ envPath = path_1.default.join(tmpDir, ".env");
72
+ });
73
+ afterEach(function () {
74
+ try {
75
+ fs_1.default.rmSync(tmpDir, { recursive: true });
76
+ }
77
+ catch (e) {
78
+ // cleanup best-effort
79
+ }
80
+ });
81
+ it("writes multiple vars in a single call", function () {
82
+ (0, FileUtils_1.writeEnvVars)({
83
+ vars: [
84
+ { key: "A", value: "1" },
85
+ { key: "B", value: "2" },
86
+ { key: "C", value: "3" },
87
+ ],
88
+ envPath,
89
+ });
90
+ const content = fs_1.default.readFileSync(envPath, "utf8");
91
+ expect(content).toContain("A=1");
92
+ expect(content).toContain("B=2");
93
+ expect(content).toContain("C=3");
94
+ });
95
+ it("merges into existing file preserving unrelated keys", function () {
96
+ fs_1.default.writeFileSync(envPath, "KEEP=yes\nUPDATE=old\n", "utf8");
97
+ (0, FileUtils_1.writeEnvVars)({
98
+ vars: [
99
+ { key: "UPDATE", value: "new" },
100
+ { key: "ADDED", value: "fresh" },
101
+ ],
102
+ envPath,
103
+ });
104
+ const content = fs_1.default.readFileSync(envPath, "utf8");
105
+ expect(content).toContain("KEEP=yes");
106
+ expect(content).toContain("UPDATE=new");
107
+ expect(content).toContain("ADDED=fresh");
108
+ expect(content).not.toContain("UPDATE=old");
109
+ });
110
+ it("handles empty file", function () {
111
+ fs_1.default.writeFileSync(envPath, "", "utf8");
112
+ (0, FileUtils_1.writeEnvVars)({
113
+ vars: [{ key: "X", value: "y" }],
114
+ envPath,
115
+ });
116
+ const content = fs_1.default.readFileSync(envPath, "utf8");
117
+ expect(content).toBe("X=y\n");
118
+ });
119
+ });
package/dist/wizard.js CHANGED
@@ -1,85 +1,12 @@
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
  };
38
5
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.startWizard = startWizard;
40
6
  exports.getLoginInfo = getLoginInfo;
41
7
  exports.setupDotEnv = setupDotEnv;
42
8
  const inquirer_1 = __importDefault(require("inquirer"));
43
- const ConfigManager = __importStar(require("./config"));
44
- const AppUtils = __importStar(require("./appUtils"));
45
- const fs_1 = __importDefault(require("fs"));
46
- const fsp = fs_1.default.promises;
47
- const Logger_1 = require("./Logger");
48
- const path_1 = __importDefault(require("path"));
49
- const snClient_1 = require("./snClient");
50
- async function startWizard() {
51
- let loginAnswers = await getLoginInfo();
52
- try {
53
- let { username, password, instance } = loginAnswers;
54
- let instanceUrl = instance.replace("https://", "").replace("http://", "");
55
- if (!instanceUrl.endsWith("/")) {
56
- instanceUrl += `/`;
57
- }
58
- const client = (0, snClient_1.snClient)(instanceUrl, username, password);
59
- const apps = await (0, snClient_1.unwrapSNResponse)(client.getAppList());
60
- await setupDotEnv(loginAnswers);
61
- let hasConfig = await checkConfig();
62
- if (!hasConfig) {
63
- Logger_1.logger.info("Generating config...");
64
- await writeDefaultConfig(hasConfig);
65
- }
66
- let man = ConfigManager.getManifest(true);
67
- if (!man) {
68
- let selectedApp = await showAppList(apps);
69
- if (!selectedApp) {
70
- return;
71
- }
72
- Logger_1.logger.info("Downloading app...");
73
- await downloadApp(loginAnswers, selectedApp);
74
- }
75
- Logger_1.logger.success("You are all set up 👍 Try running 'npx sinc dev' to begin development mode.");
76
- await ConfigManager.loadConfigs();
77
- }
78
- catch (e) {
79
- Logger_1.logger.error("Failed to setup application. Check to see that your credentials are correct and you have the update set installed on your instance.");
80
- return;
81
- }
82
- }
9
+ const FileUtils_1 = require("./FileUtils");
83
10
  async function getLoginInfo() {
84
11
  return await inquirer_1.default.prompt([
85
12
  {
@@ -99,80 +26,15 @@ async function getLoginInfo() {
99
26
  },
100
27
  ]);
101
28
  }
102
- async function checkConfig() {
103
- try {
104
- let checkConfig = ConfigManager.checkConfigPath();
105
- if (!checkConfig) {
106
- return false;
107
- }
108
- await fsp.access(checkConfig, fs_1.default.constants.F_OK);
109
- return true;
110
- }
111
- catch (e) {
112
- return false;
113
- }
114
- }
115
29
  async function setupDotEnv(answers) {
116
- let data = `SN_USER=${answers.username}
117
- SN_PASSWORD=${answers.password}
118
- SN_INSTANCE=${answers.instance}
119
- `;
120
30
  process.env.SN_USER = answers.username;
121
31
  process.env.SN_PASSWORD = answers.password;
122
32
  process.env.SN_INSTANCE = answers.instance;
123
- try {
124
- await fsp.writeFile(ConfigManager.getEnvPath(), data);
125
- }
126
- catch (e) {
127
- throw e;
128
- }
129
- }
130
- async function writeDefaultConfig(hasConfig) {
131
- try {
132
- let pth;
133
- if (hasConfig)
134
- pth = ConfigManager.getConfigPath();
135
- else
136
- pth = path_1.default.join(process.cwd(), "sinc.config.js");
137
- if (pth) {
138
- await fsp.writeFile(pth, ConfigManager.getDefaultConfigFile());
139
- }
140
- }
141
- catch (e) {
142
- throw e;
143
- }
144
- }
145
- async function showAppList(apps) {
146
- let appSelection = await inquirer_1.default.prompt([
147
- {
148
- type: "list",
149
- name: "app",
150
- message: "Which app would you like to work with?",
151
- choices: apps.map((app) => {
152
- return {
153
- name: `${app.displayName}(${app.scope})`,
154
- value: app.scope,
155
- short: app.displayName,
156
- };
157
- }),
158
- },
159
- ]);
160
- return appSelection.app;
161
- }
162
- async function downloadApp(answers, scope) {
163
- try {
164
- const client = (0, snClient_1.defaultClient)();
165
- const config = ConfigManager.getConfig();
166
- const man = await (0, snClient_1.unwrapSNResponse)(client.getManifest(scope, config, true));
167
- await AppUtils.processManifest(man);
168
- }
169
- catch (e) {
170
- let message;
171
- if (e instanceof Error)
172
- message = e.message;
173
- else
174
- message = String(e);
175
- Logger_1.logger.error(message);
176
- throw new Error("Failed to download files!");
177
- }
33
+ (0, FileUtils_1.writeEnvVars)({
34
+ vars: [
35
+ { key: "SN_USER", value: answers.username },
36
+ { key: "SN_PASSWORD", value: answers.password },
37
+ { key: "SN_INSTANCE", value: answers.instance },
38
+ ],
39
+ });
178
40
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tenonhq/sincronia-core",
3
- "version": "0.0.68",
3
+ "version": "0.0.69",
4
4
  "description": "Next-gen file syncer",
5
5
  "license": "GPL-3.0",
6
6
  "main": "./dist/index.js",
@@ -39,7 +39,6 @@
39
39
  },
40
40
  "dependencies": {
41
41
  "@tenonhq/sincronia-clickup": "^0.0.1",
42
- "@tenonhq/sincronia-core": "file:",
43
42
  "@tenonhq/sincronia-schema": "^0.0.1",
44
43
  "axios": "^1.5.1",
45
44
  "axios-cookiejar-support": "^4.0.7",