viruagent-cli 0.6.0 → 0.6.1

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.
@@ -15,7 +15,7 @@ const { enrichContentWithUploadedImages, resolveMandatoryThumbnail } = require('
15
15
  const { createWithProviderSession } = require('./session');
16
16
  const { createAskForAuthentication } = require('./auth');
17
17
 
18
- const createTistoryProvider = ({ sessionPath }) => {
18
+ const createTistoryProvider = ({ sessionPath, account }) => {
19
19
  const tistoryApi = createTistoryApiClient({ sessionPath });
20
20
 
21
21
  const pending2faResult = (mode = 'kakao') => ({
@@ -33,7 +33,7 @@ const createTistoryProvider = ({ sessionPath }) => {
33
33
  pending2faResult,
34
34
  });
35
35
 
36
- const withProviderSession = createWithProviderSession(askForAuthentication);
36
+ const withProviderSession = createWithProviderSession(askForAuthentication, account);
37
37
 
38
38
  return {
39
39
  id: 'tistory',
@@ -49,7 +49,7 @@ const createTistoryProvider = ({ sessionPath }) => {
49
49
  blogName,
50
50
  blogUrl: `https://${blogName}.tistory.com`,
51
51
  sessionPath,
52
- metadata: getProviderMeta('tistory') || {},
52
+ metadata: getProviderMeta('tistory', account) || {},
53
53
  };
54
54
  } catch (error) {
55
55
  return {
@@ -57,7 +57,7 @@ const createTistoryProvider = ({ sessionPath }) => {
57
57
  loggedIn: false,
58
58
  sessionPath,
59
59
  error: error.message,
60
- metadata: getProviderMeta('tistory') || {},
60
+ metadata: getProviderMeta('tistory', account) || {},
61
61
  };
62
62
  }
63
63
  });
@@ -89,7 +89,7 @@ const createTistoryProvider = ({ sessionPath }) => {
89
89
  blogName: result.blogName,
90
90
  blogUrl: result.blogUrl,
91
91
  sessionPath: result.sessionPath,
92
- });
92
+ }, account);
93
93
  return result;
94
94
  },
95
95
 
@@ -529,7 +529,7 @@ const createTistoryProvider = ({ sessionPath }) => {
529
529
  },
530
530
 
531
531
  async logout() {
532
- clearProviderMeta('tistory');
532
+ clearProviderMeta('tistory', account);
533
533
  return {
534
534
  provider: 'tistory',
535
535
  loggedOut: true,
@@ -3,19 +3,27 @@ const path = require('path');
3
3
  const { saveProviderMeta } = require('../../storage/sessionStore');
4
4
  const { sleep, readCredentialsFromEnv, parseSessionError, buildLoginErrorMessage } = require('./utils');
5
5
  const { clickKakaoAccountContinue } = require('./browserHelpers');
6
+ const { extractAllCookies, filterCookies, cookiesToSessionFormat } = require('../chromeManager');
6
7
 
7
- const isLoggedInByCookies = async (context) => {
8
- const cookies = await context.cookies('https://www.tistory.com');
9
- return cookies.some((cookie) => {
10
- const name = cookie.name.toLowerCase();
11
- return name.includes('tistory') || name.includes('access') || name.includes('login');
12
- });
8
+ const isLoggedInByCookies = async (context, page) => {
9
+ try {
10
+ // Use CDP to get all cookies including httpOnly (TSSESSION)
11
+ const all = await extractAllCookies(context, page);
12
+ return all.some((c) => c.domain.includes('tistory') && c.name === 'TSSESSION');
13
+ } catch {
14
+ // Fallback to context.cookies if CDP fails
15
+ const cookies = await context.cookies('https://www.tistory.com');
16
+ return cookies.some((cookie) => {
17
+ const name = cookie.name.toLowerCase();
18
+ return name.includes('tistory') || name.includes('access') || name.includes('login');
19
+ });
20
+ }
13
21
  };
14
22
 
15
23
  const waitForLoginFinish = async (page, context, timeoutMs = 45000) => {
16
24
  const deadline = Date.now() + timeoutMs;
17
25
  while (Date.now() < deadline) {
18
- if (await isLoggedInByCookies(context)) {
26
+ if (await isLoggedInByCookies(context, page)) {
19
27
  return true;
20
28
  }
21
29
 
@@ -33,16 +41,11 @@ const waitForLoginFinish = async (page, context, timeoutMs = 45000) => {
33
41
  return false;
34
42
  };
35
43
 
36
- const persistTistorySession = async (context, targetSessionPath) => {
37
- const cookies = await context.cookies('https://www.tistory.com');
38
- const sanitized = cookies.map((cookie) => ({
39
- ...cookie,
40
- expires: Number(cookie.expires || -1),
41
- size: undefined,
42
- partitionKey: undefined,
43
- sourcePort: undefined,
44
- sourceScheme: undefined,
45
- }));
44
+ const persistTistorySession = async (context, page, targetSessionPath) => {
45
+ // Use CDP Network.getAllCookies to capture httpOnly cookies (e.g. TSSESSION)
46
+ const all = await extractAllCookies(context, page);
47
+ const tistoryCookies = filterCookies(all, ['tistory.com']);
48
+ const sanitized = cookiesToSessionFormat(tistoryCookies);
46
49
 
47
50
  const payload = {
48
51
  cookies: sanitized,
@@ -60,16 +63,13 @@ const persistTistorySession = async (context, targetSessionPath) => {
60
63
  * withProviderSession factory.
61
64
  * Receives askForAuthentication via dependency injection to avoid scope issues.
62
65
  */
63
- const createWithProviderSession = (askForAuthentication) => async (fn) => {
66
+ const createWithProviderSession = (askForAuthentication, account) => async (fn) => {
64
67
  const credentials = readCredentialsFromEnv();
65
68
  const hasCredentials = Boolean(credentials.username && credentials.password);
66
69
 
67
70
  try {
68
71
  const result = await fn();
69
- saveProviderMeta('tistory', {
70
- loggedIn: true,
71
- lastValidatedAt: new Date().toISOString(),
72
- });
72
+ saveProviderMeta('tistory', { loggedIn: true, lastValidatedAt: new Date().toISOString() }, account);
73
73
  return result;
74
74
  } catch (error) {
75
75
  if (!parseSessionError(error) || !hasCredentials) {
@@ -91,7 +91,7 @@ const createWithProviderSession = (askForAuthentication) => async (fn) => {
91
91
  sessionPath: loginResult.sessionPath,
92
92
  lastRefreshedAt: new Date().toISOString(),
93
93
  lastError: null,
94
- });
94
+ }, account);
95
95
 
96
96
  if (!loginResult.loggedIn) {
97
97
  throw new Error(loginResult.message || 'Login status could not be confirmed after session refresh.');
@@ -103,7 +103,7 @@ const createWithProviderSession = (askForAuthentication) => async (fn) => {
103
103
  loggedIn: false,
104
104
  lastError: buildLoginErrorMessage(reloginError),
105
105
  lastValidatedAt: new Date().toISOString(),
106
- });
106
+ }, account);
107
107
  throw reloginError;
108
108
  }
109
109
  }
package/src/runner.js CHANGED
@@ -86,9 +86,10 @@ const runCommand = async (command, opts = {}) => {
86
86
  }
87
87
 
88
88
  const providerName = opts.provider || 'tistory';
89
+ const account = opts.account || undefined;
89
90
  let provider;
90
91
  try {
91
- provider = manager.getProvider(providerName);
92
+ provider = manager.getProvider(providerName, account);
92
93
  } catch {
93
94
  throw createError(
94
95
  'PROVIDER_NOT_FOUND',
@@ -15,23 +15,25 @@ const providers = ['tistory', 'naver', 'insta'];
15
15
  const createProviderManager = () => {
16
16
  const cache = new Map();
17
17
 
18
- const getProvider = (provider = 'tistory') => {
18
+ const getProvider = (provider = 'tistory', account) => {
19
19
  const normalized = String(provider || 'tistory').toLowerCase();
20
20
  if (!providerFactory[normalized]) {
21
21
  throw new Error(`Unsupported provider: ${provider}. Available options: ${providers.join(', ')}`);
22
22
  }
23
23
 
24
- if (!cache.has(normalized)) {
25
- const sessionPath = getSessionPath(normalized);
24
+ const cacheKey = account ? `${normalized}:${String(account).toLowerCase()}` : normalized;
25
+ if (!cache.has(cacheKey)) {
26
+ const sessionPath = getSessionPath(normalized, account);
26
27
  const options = {
27
28
  provider: normalized,
28
29
  sessionPath,
30
+ account: account || undefined,
29
31
  };
30
32
  const providerInstance = providerFactory[normalized](options);
31
- cache.set(normalized, providerInstance);
33
+ cache.set(cacheKey, providerInstance);
32
34
  }
33
35
 
34
- return cache.get(normalized);
36
+ return cache.get(cacheKey);
35
37
  };
36
38
 
37
39
  const providerNames = { tistory: 'Tistory', naver: 'Naver Blog', insta: 'Instagram' };
@@ -27,9 +27,11 @@ const writeJson = (target, data) => {
27
27
  fs.writeFileSync(target, JSON.stringify(data, null, 2), 'utf-8');
28
28
  };
29
29
 
30
- const getSessionPath = (provider) => {
30
+ const getSessionPath = (provider, account) => {
31
31
  ensureDir(SESSION_DIR);
32
- return path.join(SESSION_DIR, `${normalizeProvider(provider)}-session.json`);
32
+ const base = normalizeProvider(provider);
33
+ const suffix = account ? `-${String(account).toLowerCase().replace(/[^a-z0-9._-]/g, '_')}` : '';
34
+ return path.join(SESSION_DIR, `${base}${suffix}-session.json`);
33
35
  };
34
36
 
35
37
  const getProvidersMeta = () => {
@@ -37,25 +39,32 @@ const getProvidersMeta = () => {
37
39
  return readJson(META_FILE);
38
40
  };
39
41
 
40
- const saveProviderMeta = (provider, patch) => {
42
+ const metaKey = (provider, account) => {
43
+ const base = normalizeProvider(provider);
44
+ return account ? `${base}:${String(account).toLowerCase()}` : base;
45
+ };
46
+
47
+ const saveProviderMeta = (provider, patch, account) => {
41
48
  const meta = getProvidersMeta();
42
- meta[normalizeProvider(provider)] = {
43
- ...(meta[normalizeProvider(provider)] || {}),
49
+ const key = metaKey(provider, account);
50
+ meta[key] = {
51
+ ...(meta[key] || {}),
44
52
  ...patch,
45
53
  provider: normalizeProvider(provider),
54
+ ...(account ? { account: String(account).toLowerCase() } : {}),
46
55
  updatedAt: new Date().toISOString(),
47
56
  };
48
57
  writeJson(META_FILE, meta);
49
58
  };
50
59
 
51
- const getProviderMeta = (provider) => {
60
+ const getProviderMeta = (provider, account) => {
52
61
  const meta = getProvidersMeta();
53
- return meta[normalizeProvider(provider)] || null;
62
+ return meta[metaKey(provider, account)] || null;
54
63
  };
55
64
 
56
- const clearProviderMeta = (provider) => {
65
+ const clearProviderMeta = (provider, account) => {
57
66
  const meta = getProvidersMeta();
58
- delete meta[normalizeProvider(provider)];
67
+ delete meta[metaKey(provider, account)];
59
68
  writeJson(META_FILE, meta);
60
69
  };
61
70