@sstar/skill-install 1.0.0

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.
@@ -0,0 +1,260 @@
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.AuthService = void 0;
37
+ const cheerio = __importStar(require("cheerio"));
38
+ const errors_1 = require("../core/errors");
39
+ const logger_1 = require("../core/logger");
40
+ class AuthService {
41
+ constructor(http, config) {
42
+ this.http = http;
43
+ this.config = config;
44
+ this.logger = new logger_1.Logger('AuthService');
45
+ this.authInFlight = null;
46
+ this.session = {
47
+ cookies: [],
48
+ isAuthenticated: false,
49
+ lastAuthTime: 0
50
+ };
51
+ }
52
+ async ensureAuthenticated() {
53
+ try {
54
+ const isValid = await this.isSessionValid();
55
+ if (!isValid) {
56
+ this.logger.info('Session invalid, attempting to re-authenticate');
57
+ if (!this.authInFlight) {
58
+ this.authInFlight = this.authenticate().finally(() => {
59
+ this.authInFlight = null;
60
+ });
61
+ }
62
+ const success = await this.authInFlight;
63
+ if (!success) {
64
+ throw new errors_1.WikiError(errors_1.ErrorType.AUTHENTICATION, 'Failed to authenticate with Confluence');
65
+ }
66
+ this.logger.info('Re-authentication successful');
67
+ }
68
+ else {
69
+ this.logger.debug('Session is valid, no re-authentication needed');
70
+ }
71
+ }
72
+ catch (error) {
73
+ this.logger.error('Authentication check failed:', error);
74
+ throw new errors_1.WikiError(errors_1.ErrorType.AUTHENTICATION, `Authentication failed: ${error.message}`, error);
75
+ }
76
+ }
77
+ async authenticate() {
78
+ try {
79
+ this.logger.info('Starting authentication process');
80
+ const isValid = await this.isSessionValid();
81
+ if (isValid) {
82
+ this.logger.info('Existing session is still valid');
83
+ return true;
84
+ }
85
+ this.logger.info('Performing fresh authentication');
86
+ const loginData = await this.getLoginFormData();
87
+ const success = await this.performLogin(loginData);
88
+ if (success) {
89
+ this.session.isAuthenticated = true;
90
+ this.session.lastAuthTime = Date.now();
91
+ this.session.cookies = this.http.getCookies();
92
+ this.logger.info('Authentication successful');
93
+ return true;
94
+ }
95
+ this.logger.error('Authentication failed');
96
+ return false;
97
+ }
98
+ catch (error) {
99
+ this.logger.error('Authentication error:', error);
100
+ this.session.isAuthenticated = false;
101
+ throw error;
102
+ }
103
+ }
104
+ async getLoginFormData() {
105
+ try {
106
+ this.logger.debug('Fetching login page');
107
+ const response = await this.http.get('/login.action');
108
+ const $ = cheerio.load(response.data);
109
+ const loginData = {
110
+ username: this.config.username,
111
+ password: this.config.password
112
+ };
113
+ // Extract CSRF token
114
+ const csrfToken = $('meta[name="ajs-atl-token"]').attr('content') ||
115
+ $('meta[name="atlassian-token"]').attr('content') ||
116
+ $('input[name="atl_token"]').attr('value') ||
117
+ $('meta[name="_token"]').attr('content');
118
+ if (csrfToken) {
119
+ loginData.csrfToken = csrfToken;
120
+ loginData['atl_token'] = csrfToken;
121
+ this.logger.debug('Found CSRF token');
122
+ }
123
+ else {
124
+ this.logger.warn('No CSRF token found in login page');
125
+ }
126
+ // Extract all hidden fields from the login form
127
+ $('form#loginform input[type="hidden"], form[name="loginform"] input[type="hidden"]').each((_, element) => {
128
+ const name = $(element).attr('name');
129
+ const value = $(element).attr('value');
130
+ if (name && value && !loginData[name]) {
131
+ loginData[name] = value;
132
+ this.logger.debug(`Found hidden field: ${name}`);
133
+ }
134
+ });
135
+ // Extract destination field
136
+ const destination = $('input[name="os_destination"]').attr('value');
137
+ if (destination) {
138
+ loginData['os_destination'] = destination;
139
+ this.logger.debug('Found destination:', destination);
140
+ }
141
+ return loginData;
142
+ }
143
+ catch (error) {
144
+ this.logger.error('Failed to retrieve login form data:', error);
145
+ throw new errors_1.WikiError(errors_1.ErrorType.AUTHENTICATION, 'Failed to retrieve login form data', error);
146
+ }
147
+ }
148
+ async performLogin(loginData) {
149
+ try {
150
+ this.logger.debug('Submitting login form');
151
+ // Format login data - use correct field names for Confluence
152
+ const formData = {
153
+ 'os_username': loginData.username,
154
+ 'os_password': loginData.password,
155
+ 'os_cookie': 'true',
156
+ 'login': 'Log in'
157
+ };
158
+ // Add CSRF token
159
+ if (loginData.csrfToken) {
160
+ formData['atl_token'] = loginData.csrfToken;
161
+ }
162
+ // Add any additional hidden fields
163
+ Object.keys(loginData).forEach(key => {
164
+ if (key !== 'username' && key !== 'password' && key !== 'csrfToken' && !formData[key]) {
165
+ const value = loginData[key];
166
+ if (value) {
167
+ formData[key] = value;
168
+ }
169
+ }
170
+ });
171
+ const response = await this.http.post('/dologin.action', new URLSearchParams(formData).toString(), {
172
+ headers: {
173
+ 'Content-Type': 'application/x-www-form-urlencoded',
174
+ 'Referer': `${this.config.baseUrl}/login.action`
175
+ },
176
+ maxRedirects: 0,
177
+ validateStatus: (status) => status >= 200 && status < 400
178
+ });
179
+ const isSuccess = this.checkLoginSuccess(response.data, response.status);
180
+ if (isSuccess) {
181
+ this.logger.info('Login successful');
182
+ return true;
183
+ }
184
+ this.logger.warn('Login failed - invalid credentials or form submission');
185
+ return false;
186
+ }
187
+ catch (error) {
188
+ if (error.response?.status === 302) {
189
+ const location = error.response.headers.location;
190
+ this.logger.debug('Redirect response:', { status: 302, location });
191
+ if (location && !location.includes('login') && !location.includes('error')) {
192
+ this.logger.info('Login successful (redirect detected)');
193
+ return true;
194
+ }
195
+ }
196
+ this.logger.error('Login request failed:', error);
197
+ throw new errors_1.WikiError(errors_1.ErrorType.AUTHENTICATION, 'Login request failed', error);
198
+ }
199
+ }
200
+ checkLoginSuccess(responseData, statusCode) {
201
+ if (statusCode === 302) {
202
+ return true;
203
+ }
204
+ const $ = cheerio.load(responseData);
205
+ const hasLoginError = $('.error').length > 0 ||
206
+ $('.aui-message-error').length > 0 ||
207
+ $('#loginform').length > 0;
208
+ const hasSuccessIndicators = $('.aui-nav-heading').length > 0 ||
209
+ $('#header').length > 0 ||
210
+ $('[data-aui-component="navigation"]').length > 0;
211
+ return !hasLoginError && hasSuccessIndicators;
212
+ }
213
+ async isSessionValid() {
214
+ if (!this.session.isAuthenticated) {
215
+ return false;
216
+ }
217
+ const sessionAge = Date.now() - this.session.lastAuthTime;
218
+ const maxAge = 30 * 60 * 1000; // 30 minutes
219
+ if (sessionAge > maxAge) {
220
+ this.logger.debug('Session expired due to age');
221
+ this.session.isAuthenticated = false;
222
+ return false;
223
+ }
224
+ try {
225
+ this.logger.debug('Validating session by accessing dashboard');
226
+ const response = await this.http.get('/dashboard.action');
227
+ const $ = cheerio.load(response.data);
228
+ // Check if we're on a login page
229
+ const isLoginPage = $('#loginform').length > 0 ||
230
+ response.data.includes('Log in - Confluence') ||
231
+ response.data.includes('class="login');
232
+ // Check for authenticated user indicators
233
+ const isLoggedIn = !isLoginPage && ($('.aui-nav-heading').length > 0 ||
234
+ $('[data-aui-component="navigation"]').length > 0 ||
235
+ $('.aui-header').length > 0);
236
+ if (!isLoggedIn) {
237
+ this.logger.debug('Session validation failed - not logged in');
238
+ this.session.isAuthenticated = false;
239
+ return false;
240
+ }
241
+ this.logger.debug('Session is valid');
242
+ return true;
243
+ }
244
+ catch (error) {
245
+ this.logger.warn('Session validation failed:', error);
246
+ this.session.isAuthenticated = false;
247
+ return false;
248
+ }
249
+ }
250
+ clearSession() {
251
+ this.session = {
252
+ cookies: [],
253
+ isAuthenticated: false,
254
+ lastAuthTime: 0
255
+ };
256
+ this.http.clearCookies();
257
+ this.logger.info('Session cleared');
258
+ }
259
+ }
260
+ exports.AuthService = AuthService;
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};