@unito/integration-cli 0.64.3 → 0.64.5
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/boilerplate/package-lock.json +223 -310
- package/dist/src/commands/publish.js +2 -1
- package/dist/src/hooks/init/displayLogo.js +2 -0
- package/dist/src/hooks/init/updateNotifier.d.ts +19 -0
- package/dist/src/hooks/init/updateNotifier.js +156 -0
- package/dist/src/services/integrationsPlatformClient.d.ts +4 -0
- package/dist/src/services/oauth2.d.ts +7 -0
- package/dist/src/services/oauth2.js +23 -6
- package/dist/test/helpers/init.js +1 -0
- package/dist/test/hooks/updateNotifier.test.d.ts +1 -0
- package/dist/test/hooks/updateNotifier.test.js +356 -0
- package/dist/test/services/oauth2.test.js +43 -3
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
|
@@ -271,10 +271,11 @@ class Publish extends baseCommand_1.BaseCommand {
|
|
|
271
271
|
}
|
|
272
272
|
archiveIntegration() {
|
|
273
273
|
const archivePath = tmp_1.default.tmpNameSync({ postfix: `.zip` });
|
|
274
|
-
child_process_1.default.execSync(`git ls-files ':!:docs/' --cached --others --exclude-standard | zip -@ ${archivePath}`, {
|
|
274
|
+
child_process_1.default.execSync(`git ls-files ':!:docs/' ':!:test/' --cached --others --exclude-standard | zip -@ ${archivePath}`, {
|
|
275
275
|
cwd: process.cwd(),
|
|
276
276
|
env: { ...process.env },
|
|
277
277
|
});
|
|
278
|
+
core_1.ux.log(chalk_1.default.yellowBright("Note: the 'docs' and 'test' folders are excluded from the published package."));
|
|
278
279
|
return archivePath;
|
|
279
280
|
}
|
|
280
281
|
hashEmail(email) {
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
4
|
const core_1 = require("@oclif/core");
|
|
5
5
|
const gradient = tslib_1.__importStar(require("gradient-string"));
|
|
6
|
+
const updateNotifier_1 = require("./updateNotifier");
|
|
6
7
|
const displayLogo = async function () {
|
|
7
8
|
const gradients = [
|
|
8
9
|
gradient.atlas,
|
|
@@ -33,5 +34,6 @@ const displayLogo = async function () {
|
|
|
33
34
|
'+------------------------------------+',
|
|
34
35
|
].join('\n'));
|
|
35
36
|
core_1.ux.log(UNITOCLI_LOGO);
|
|
37
|
+
(0, updateNotifier_1.checkForUpdate)(this.config.version);
|
|
36
38
|
};
|
|
37
39
|
exports.default = displayLogo;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import child_process from 'child_process';
|
|
2
|
+
interface UpdateCache {
|
|
3
|
+
latest: string;
|
|
4
|
+
checkedAt: number;
|
|
5
|
+
}
|
|
6
|
+
export interface UpdateNotifierDeps {
|
|
7
|
+
cacheFile: string;
|
|
8
|
+
spawn: typeof child_process.spawn;
|
|
9
|
+
log: typeof console.log;
|
|
10
|
+
upgrade: () => void;
|
|
11
|
+
}
|
|
12
|
+
export declare function isNewerVersion(latest: string, current: string): boolean;
|
|
13
|
+
export declare function readCache(cacheFile: string): UpdateCache | null;
|
|
14
|
+
export declare function isCacheStale(cache: UpdateCache | null, intervalMs?: number): boolean;
|
|
15
|
+
export declare function stripAnsi(str: string): string;
|
|
16
|
+
export declare function formatNotification(current: string, latest: string): string;
|
|
17
|
+
export declare function spawnBackgroundCheck(deps: UpdateNotifierDeps): void;
|
|
18
|
+
export declare function checkForUpdate(currentVersion: string, overrides?: Partial<UpdateNotifierDeps>): void;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isNewerVersion = isNewerVersion;
|
|
4
|
+
exports.readCache = readCache;
|
|
5
|
+
exports.isCacheStale = isCacheStale;
|
|
6
|
+
exports.stripAnsi = stripAnsi;
|
|
7
|
+
exports.formatNotification = formatNotification;
|
|
8
|
+
exports.spawnBackgroundCheck = spawnBackgroundCheck;
|
|
9
|
+
exports.checkForUpdate = checkForUpdate;
|
|
10
|
+
const tslib_1 = require("tslib");
|
|
11
|
+
const child_process_1 = tslib_1.__importDefault(require("child_process"));
|
|
12
|
+
const fs_1 = tslib_1.__importDefault(require("fs"));
|
|
13
|
+
const os_1 = tslib_1.__importDefault(require("os"));
|
|
14
|
+
const path_1 = tslib_1.__importDefault(require("path"));
|
|
15
|
+
const core_1 = require("@oclif/core");
|
|
16
|
+
const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
17
|
+
const PACKAGE_NAME = '@unito/integration-cli';
|
|
18
|
+
const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
19
|
+
function getDefaultCacheFile() {
|
|
20
|
+
return path_1.default.join(os_1.default.homedir(), '.cache', 'integrationcli', 'update-check.json');
|
|
21
|
+
}
|
|
22
|
+
function defaultUpgrade() {
|
|
23
|
+
child_process_1.default.execSync(`npm install --force --global ${PACKAGE_NAME}`, {
|
|
24
|
+
stdio: ['ignore', 'ignore', 'pipe'],
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function getDefaultDeps() {
|
|
28
|
+
return {
|
|
29
|
+
cacheFile: getDefaultCacheFile(),
|
|
30
|
+
spawn: child_process_1.default.spawn,
|
|
31
|
+
log: core_1.ux.log,
|
|
32
|
+
upgrade: defaultUpgrade,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function isNewerVersion(latest, current) {
|
|
36
|
+
const latestParts = latest.split('.').map(Number);
|
|
37
|
+
const currentParts = current.split('.').map(Number);
|
|
38
|
+
for (let i = 0; i < 3; i++) {
|
|
39
|
+
if ((latestParts[i] || 0) > (currentParts[i] || 0))
|
|
40
|
+
return true;
|
|
41
|
+
if ((latestParts[i] || 0) < (currentParts[i] || 0))
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
function readCache(cacheFile) {
|
|
47
|
+
try {
|
|
48
|
+
const data = fs_1.default.readFileSync(cacheFile, 'utf8');
|
|
49
|
+
const parsed = JSON.parse(data);
|
|
50
|
+
if (typeof parsed?.latest !== 'string' || typeof parsed?.checkedAt !== 'number')
|
|
51
|
+
return null;
|
|
52
|
+
return parsed;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function isCacheStale(cache, intervalMs = CHECK_INTERVAL_MS) {
|
|
59
|
+
if (!cache)
|
|
60
|
+
return true;
|
|
61
|
+
return Date.now() - cache.checkedAt > intervalMs;
|
|
62
|
+
}
|
|
63
|
+
// eslint-disable-next-line no-control-regex
|
|
64
|
+
const ANSI_PATTERN = /\u001b\[[0-9;]*m/g;
|
|
65
|
+
function stripAnsi(str) {
|
|
66
|
+
return str.replace(ANSI_PATTERN, '');
|
|
67
|
+
}
|
|
68
|
+
function formatNotification(current, latest) {
|
|
69
|
+
const npmUrl = `https://www.npmjs.com/package/${PACKAGE_NAME}`;
|
|
70
|
+
const lines = [
|
|
71
|
+
'',
|
|
72
|
+
` Update available ${chalk_1.default.gray(current)} ${chalk_1.default.gray('→')} ${chalk_1.default.greenBright(latest)}`,
|
|
73
|
+
` ${chalk_1.default.cyan(npmUrl)}`,
|
|
74
|
+
'',
|
|
75
|
+
` Run ${chalk_1.default.bold('integration-cli upgrade')} to update`,
|
|
76
|
+
'',
|
|
77
|
+
];
|
|
78
|
+
const maxLen = Math.max(...lines.map(line => stripAnsi(line).length));
|
|
79
|
+
const border = chalk_1.default.yellowBright;
|
|
80
|
+
const boxLines = [
|
|
81
|
+
border(' ┌' + '─'.repeat(maxLen) + '┐'),
|
|
82
|
+
...lines.map(line => {
|
|
83
|
+
const padding = maxLen - stripAnsi(line).length;
|
|
84
|
+
return border(' │') + line + ' '.repeat(padding) + border('│');
|
|
85
|
+
}),
|
|
86
|
+
border(' └' + '─'.repeat(maxLen) + '┘'),
|
|
87
|
+
];
|
|
88
|
+
return '\n' + boxLines.join('\n') + '\n';
|
|
89
|
+
}
|
|
90
|
+
function spawnBackgroundCheck(deps) {
|
|
91
|
+
const cacheDir = path_1.default.dirname(deps.cacheFile);
|
|
92
|
+
const script = `
|
|
93
|
+
const https = require('https');
|
|
94
|
+
const fs = require('fs');
|
|
95
|
+
|
|
96
|
+
const cacheDir = ${JSON.stringify(cacheDir)};
|
|
97
|
+
const cacheFile = ${JSON.stringify(deps.cacheFile)};
|
|
98
|
+
|
|
99
|
+
const url = 'https://registry.npmjs.org/${PACKAGE_NAME.replace('/', '%2f')}';
|
|
100
|
+
|
|
101
|
+
const req = https.get(url, { headers: { Accept: 'application/vnd.npm.install-v1+json' } }, (res) => {
|
|
102
|
+
if (res.statusCode !== 200) return;
|
|
103
|
+
let data = '';
|
|
104
|
+
res.on('data', chunk => { data += chunk; });
|
|
105
|
+
res.on('end', () => {
|
|
106
|
+
try {
|
|
107
|
+
const pkg = JSON.parse(data);
|
|
108
|
+
const latest = pkg['dist-tags']?.latest;
|
|
109
|
+
if (!latest) return;
|
|
110
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
111
|
+
fs.writeFileSync(cacheFile, JSON.stringify({ latest, checkedAt: Date.now() }));
|
|
112
|
+
} catch {}
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
req.on('error', () => {});
|
|
116
|
+
req.setTimeout(10000, () => { req.destroy(); });
|
|
117
|
+
`;
|
|
118
|
+
const child = deps.spawn(process.execPath, ['-e', script], {
|
|
119
|
+
detached: true,
|
|
120
|
+
stdio: 'ignore',
|
|
121
|
+
windowsHide: true,
|
|
122
|
+
});
|
|
123
|
+
child.unref();
|
|
124
|
+
}
|
|
125
|
+
function checkForUpdate(currentVersion, overrides) {
|
|
126
|
+
try {
|
|
127
|
+
// Skip during upgrade command — it handles its own version messaging
|
|
128
|
+
if (process.argv[2] === 'upgrade')
|
|
129
|
+
return;
|
|
130
|
+
// Skip in non-interactive environments (CI, tests)
|
|
131
|
+
if (process.env.CI)
|
|
132
|
+
return;
|
|
133
|
+
const deps = { ...getDefaultDeps(), ...overrides };
|
|
134
|
+
const cache = readCache(deps.cacheFile);
|
|
135
|
+
// Auto-upgrade if we have a cached newer version
|
|
136
|
+
if (cache && isNewerVersion(cache.latest, currentVersion)) {
|
|
137
|
+
try {
|
|
138
|
+
deps.log(`Upgrading integration-cli ${currentVersion} → ${cache.latest}...`);
|
|
139
|
+
deps.upgrade();
|
|
140
|
+
deps.log(`Upgraded! The new version will be used on your next command.`);
|
|
141
|
+
}
|
|
142
|
+
catch (err) {
|
|
143
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
144
|
+
deps.log(chalk_1.default.yellow(` Auto-upgrade failed: ${reason}`));
|
|
145
|
+
deps.log(formatNotification(currentVersion, cache.latest));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// Spawn background check if cache is stale
|
|
149
|
+
if (isCacheStale(cache)) {
|
|
150
|
+
spawnBackgroundCheck(deps);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
// Never let update checking break the CLI
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -328,10 +328,14 @@ export declare function getCredentials({ pagination, filters, }?: {
|
|
|
328
328
|
unitoUserId?: string;
|
|
329
329
|
/** The id of the credential account for which this credential belongs to. */
|
|
330
330
|
credentialAccountId?: string;
|
|
331
|
+
/** The credential was created after the provided date. */
|
|
332
|
+
createdAfter?: string;
|
|
331
333
|
/** The last access to the credential account occured before the provided date. */
|
|
332
334
|
lastCredentialAccountAccessAtBefore?: string;
|
|
333
335
|
/** The first failure to access the credential account occured after the provided date. */
|
|
334
336
|
firstFailedCredentialAccountAccessAtAfter?: string;
|
|
337
|
+
/** Whether to filter archived credentials. */
|
|
338
|
+
isArchived?: boolean;
|
|
335
339
|
};
|
|
336
340
|
}, opts?: Oazapfts.RequestOpts): Promise<Pagination & {
|
|
337
341
|
/** The credentials. */
|
|
@@ -40,6 +40,7 @@ declare class OAuth2Service {
|
|
|
40
40
|
private tokenRequestParameters;
|
|
41
41
|
private refreshRequestParameters;
|
|
42
42
|
private credentialPayload;
|
|
43
|
+
private legacyRedirectUrl;
|
|
43
44
|
/**
|
|
44
45
|
* Constructs an instance of OAuthHelper.
|
|
45
46
|
* @param clientId The client ID for your OAuth application.
|
|
@@ -49,6 +50,12 @@ declare class OAuth2Service {
|
|
|
49
50
|
* @param providerTokenUrl The URL for the token endpoint of the provider.
|
|
50
51
|
*/
|
|
51
52
|
constructor(authorizationInfo: Oauth2Payload, environment?: Environment, credentialPayload?: Record<string, unknown>);
|
|
53
|
+
/**
|
|
54
|
+
* Returns the redirect URI for the OAuth2 flow.
|
|
55
|
+
* When legacyRedirectUrl is configured, uses that URL (routed through maestro).
|
|
56
|
+
* Otherwise, uses the standard platformServer callback URL.
|
|
57
|
+
*/
|
|
58
|
+
private get redirectUri();
|
|
52
59
|
/**
|
|
53
60
|
* Initiate the authorization flow and redirects the user to the provider's authorization page.
|
|
54
61
|
*/
|
|
@@ -32,6 +32,7 @@ class OAuth2Service {
|
|
|
32
32
|
tokenRequestParameters;
|
|
33
33
|
refreshRequestParameters;
|
|
34
34
|
credentialPayload;
|
|
35
|
+
legacyRedirectUrl;
|
|
35
36
|
/**
|
|
36
37
|
* Constructs an instance of OAuthHelper.
|
|
37
38
|
* @param clientId The client ID for your OAuth application.
|
|
@@ -41,7 +42,7 @@ class OAuth2Service {
|
|
|
41
42
|
* @param providerTokenUrl The URL for the token endpoint of the provider.
|
|
42
43
|
*/
|
|
43
44
|
constructor(authorizationInfo, environment = globalConfiguration_1.Environment.Production, credentialPayload) {
|
|
44
|
-
const { clientId, clientSecret, authorizationUrl, scopes, tokenUrl, grantType, requestContentType, refreshRequestParameters, tokenRequestParameters, } = authorizationInfo;
|
|
45
|
+
const { clientId, clientSecret, authorizationUrl, scopes, tokenUrl, grantType, requestContentType, refreshRequestParameters, tokenRequestParameters, legacyRedirectUrl, } = authorizationInfo;
|
|
45
46
|
this.clientId = clientId;
|
|
46
47
|
this.clientSecret = clientSecret;
|
|
47
48
|
this.providerAuthorizationUrl = authorizationUrl;
|
|
@@ -51,12 +52,22 @@ class OAuth2Service {
|
|
|
51
52
|
this.requestContentType = requestContentType ?? configurationTypes_1.RequestContentType.URL_ENCODED;
|
|
52
53
|
this.tokenRequestParameters = tokenRequestParameters;
|
|
53
54
|
this.refreshRequestParameters = refreshRequestParameters;
|
|
55
|
+
this.legacyRedirectUrl = legacyRedirectUrl;
|
|
54
56
|
this.environment = environment;
|
|
55
57
|
this.credentialPayload = credentialPayload;
|
|
56
58
|
if (!Object.values(configurationTypes_1.RequestContentType).includes(this.requestContentType)) {
|
|
57
59
|
throw new errors_1.UnsupportedContentTypeError(`Request content type not supported: ${this.requestContentType}`);
|
|
58
60
|
}
|
|
59
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Returns the redirect URI for the OAuth2 flow.
|
|
64
|
+
* When legacyRedirectUrl is configured, uses that URL (routed through maestro).
|
|
65
|
+
* Otherwise, uses the standard platformServer callback URL.
|
|
66
|
+
*/
|
|
67
|
+
get redirectUri() {
|
|
68
|
+
return (this.legacyRedirectUrl ??
|
|
69
|
+
`${IntegrationsPlatformClient.Servers[this.environment]}/credentials/new/oauth2/callback`);
|
|
70
|
+
}
|
|
60
71
|
/**
|
|
61
72
|
* Initiate the authorization flow and redirects the user to the provider's authorization page.
|
|
62
73
|
*/
|
|
@@ -71,12 +82,18 @@ class OAuth2Service {
|
|
|
71
82
|
if (this.scopes) {
|
|
72
83
|
authorizationParams.set('scope', this.scopes.join(' '));
|
|
73
84
|
}
|
|
74
|
-
const
|
|
85
|
+
const statePayload = {
|
|
75
86
|
cliCallbackUrl: `${this.serverUrl}/oauth2/callback`,
|
|
76
|
-
|
|
87
|
+
// When using legacyRedirectUrl, the callback is routed through maestro first.
|
|
88
|
+
// maestro checks for redirectToIPS in state and forwards to platformServer's oauth2Callback,
|
|
89
|
+
// which then detects cliCallbackUrl and forwards to the CLI via ngrok.
|
|
90
|
+
// TLDR: redirectToIPS makes maestro act as a pass-through relay instead of handling the callback itself.
|
|
91
|
+
...(this.legacyRedirectUrl ? { redirectToIPS: 'true' } : {}),
|
|
92
|
+
};
|
|
93
|
+
const state = Buffer.from(JSON.stringify(statePayload)).toString('base64');
|
|
77
94
|
authorizationParams.set('state', state);
|
|
78
95
|
authorizationParams.set('response_type', 'code');
|
|
79
|
-
authorizationParams.set('redirect_uri',
|
|
96
|
+
authorizationParams.set('redirect_uri', this.redirectUri);
|
|
80
97
|
const delimiter = this.providerAuthorizationUrl.includes('?') ? '&' : '?';
|
|
81
98
|
const authorizationUrlTemplate = `${this.providerAuthorizationUrl}${delimiter}${authorizationParams.toString()}`;
|
|
82
99
|
const authorizationUrl = (0, template_1.expandTemplate)(authorizationUrlTemplate, {
|
|
@@ -119,7 +136,7 @@ class OAuth2Service {
|
|
|
119
136
|
])),
|
|
120
137
|
grant_type: this.grantType,
|
|
121
138
|
code: req.query.code,
|
|
122
|
-
redirect_uri:
|
|
139
|
+
redirect_uri: this.redirectUri,
|
|
123
140
|
...(this.clientId && { client_id: this.clientId }),
|
|
124
141
|
...(this.clientSecret && { client_secret: this.clientSecret }),
|
|
125
142
|
};
|
|
@@ -235,7 +252,7 @@ class OAuth2Service {
|
|
|
235
252
|
bodyData.client_assertion = this.clientSecret;
|
|
236
253
|
bodyData.assertion = refreshToken;
|
|
237
254
|
bodyData.client_assertion_type = 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer';
|
|
238
|
-
bodyData.redirect_uri = `${IntegrationsPlatformClient.Servers[this.environment]}/credentials/new/oauth2/callback
|
|
255
|
+
bodyData.redirect_uri = `${IntegrationsPlatformClient.Servers[this.environment]}/credentials/new/oauth2/callback`;
|
|
239
256
|
}
|
|
240
257
|
const templateVariables = structuredClone(this.credentialPayload ?? {});
|
|
241
258
|
templateVariables.clientId ??= this.clientId;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|