paperkite 0.1.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.
- package/README.md +81 -0
- package/data/flows.example.yml +65 -0
- package/data/settings.example.yml +13 -0
- package/dist/data/hooks/weekend-slot.d.ts +12 -0
- package/dist/data/hooks/weekend-slot.js +107 -0
- package/dist/data/hooks/weekend-slot.js.map +1 -0
- package/dist/packages/account-health/src/index.d.ts +8 -0
- package/dist/packages/account-health/src/index.js +37 -0
- package/dist/packages/account-health/src/index.js.map +1 -0
- package/dist/packages/archive/src/archiver.d.ts +98 -0
- package/dist/packages/archive/src/archiver.js +399 -0
- package/dist/packages/archive/src/archiver.js.map +1 -0
- package/dist/packages/archive/src/config.d.ts +22 -0
- package/dist/packages/archive/src/config.js +62 -0
- package/dist/packages/archive/src/config.js.map +1 -0
- package/dist/packages/archive/src/console/live.d.ts +24 -0
- package/dist/packages/archive/src/console/live.js +144 -0
- package/dist/packages/archive/src/console/live.js.map +1 -0
- package/dist/packages/archive/src/console/media.d.ts +66 -0
- package/dist/packages/archive/src/console/media.js +368 -0
- package/dist/packages/archive/src/console/media.js.map +1 -0
- package/dist/packages/archive/src/console/server.d.ts +12 -0
- package/dist/packages/archive/src/console/server.js +583 -0
- package/dist/packages/archive/src/console/server.js.map +1 -0
- package/dist/packages/archive/src/console/service.d.ts +13 -0
- package/dist/packages/archive/src/console/service.js +86 -0
- package/dist/packages/archive/src/console/service.js.map +1 -0
- package/dist/packages/archive/src/storage/index.d.ts +6 -0
- package/dist/packages/archive/src/storage/index.js +23 -0
- package/dist/packages/archive/src/storage/index.js.map +1 -0
- package/dist/packages/archive/src/storage/model.d.ts +296 -0
- package/dist/packages/archive/src/storage/model.js +151 -0
- package/dist/packages/archive/src/storage/model.js.map +1 -0
- package/dist/packages/archive/src/storage/postgres.d.ts +67 -0
- package/dist/packages/archive/src/storage/postgres.js +1021 -0
- package/dist/packages/archive/src/storage/postgres.js.map +1 -0
- package/dist/packages/archive/src/storage/sqlite.d.ts +59 -0
- package/dist/packages/archive/src/storage/sqlite.js +981 -0
- package/dist/packages/archive/src/storage/sqlite.js.map +1 -0
- package/dist/packages/notify-bark/src/index.d.ts +33 -0
- package/dist/packages/notify-bark/src/index.js +96 -0
- package/dist/packages/notify-bark/src/index.js.map +1 -0
- package/dist/packages/runtime-console/src/console/errors.d.ts +5 -0
- package/dist/packages/runtime-console/src/console/errors.js +11 -0
- package/dist/packages/runtime-console/src/console/errors.js.map +1 -0
- package/dist/packages/runtime-console/src/console/logs.d.ts +1 -0
- package/dist/packages/runtime-console/src/console/logs.js +27 -0
- package/dist/packages/runtime-console/src/console/logs.js.map +1 -0
- package/dist/packages/runtime-console/src/console/server.d.ts +6 -0
- package/dist/packages/runtime-console/src/console/server.js +182 -0
- package/dist/packages/runtime-console/src/console/server.js.map +1 -0
- package/dist/packages/runtime-console/src/index.d.ts +10 -0
- package/dist/packages/runtime-console/src/index.js +83 -0
- package/dist/packages/runtime-console/src/index.js.map +1 -0
- package/dist/src/app.d.ts +15 -0
- package/dist/src/app.js +34 -0
- package/dist/src/app.js.map +1 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +250 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/config/editor.d.ts +2 -0
- package/dist/src/config/editor.js +19 -0
- package/dist/src/config/editor.js.map +1 -0
- package/dist/src/config/loader.d.ts +6 -0
- package/dist/src/config/loader.js +188 -0
- package/dist/src/config/loader.js.map +1 -0
- package/dist/src/config/model.d.ts +71 -0
- package/dist/src/config/model.js +97 -0
- package/dist/src/config/model.js.map +1 -0
- package/dist/src/config/paths.d.ts +3 -0
- package/dist/src/config/paths.js +12 -0
- package/dist/src/config/paths.js.map +1 -0
- package/dist/src/config/settings.d.ts +19 -0
- package/dist/src/config/settings.js +44 -0
- package/dist/src/config/settings.js.map +1 -0
- package/dist/src/control/process-lock.d.ts +4 -0
- package/dist/src/control/process-lock.js +57 -0
- package/dist/src/control/process-lock.js.map +1 -0
- package/dist/src/control/socket.d.ts +14 -0
- package/dist/src/control/socket.js +131 -0
- package/dist/src/control/socket.js.map +1 -0
- package/dist/src/engine/hooks.d.ts +4 -0
- package/dist/src/engine/hooks.js +38 -0
- package/dist/src/engine/hooks.js.map +1 -0
- package/dist/src/engine/logger.d.ts +17 -0
- package/dist/src/engine/logger.js +78 -0
- package/dist/src/engine/logger.js.map +1 -0
- package/dist/src/engine/runtime.d.ts +78 -0
- package/dist/src/engine/runtime.js +799 -0
- package/dist/src/engine/runtime.js.map +1 -0
- package/dist/src/engine/scheduler.d.ts +9 -0
- package/dist/src/engine/scheduler.js +58 -0
- package/dist/src/engine/scheduler.js.map +1 -0
- package/dist/src/extensions/loader.d.ts +11 -0
- package/dist/src/extensions/loader.js +202 -0
- package/dist/src/extensions/loader.js.map +1 -0
- package/dist/src/extensions/manager.d.ts +1 -0
- package/dist/src/extensions/manager.js +121 -0
- package/dist/src/extensions/manager.js.map +1 -0
- package/dist/src/extensions/profile.d.ts +14 -0
- package/dist/src/extensions/profile.js +57 -0
- package/dist/src/extensions/profile.js.map +1 -0
- package/dist/src/extensions/registry.d.ts +19 -0
- package/dist/src/extensions/registry.js +62 -0
- package/dist/src/extensions/registry.js.map +1 -0
- package/dist/src/telegram/client.d.ts +20 -0
- package/dist/src/telegram/client.js +54 -0
- package/dist/src/telegram/client.js.map +1 -0
- package/dist/src/telegram/failure.d.ts +2 -0
- package/dist/src/telegram/failure.js +98 -0
- package/dist/src/telegram/failure.js.map +1 -0
- package/dist/src/telegram/login.d.ts +1 -0
- package/dist/src/telegram/login.js +31 -0
- package/dist/src/telegram/login.js.map +1 -0
- package/dist/src/telegram/pool.d.ts +47 -0
- package/dist/src/telegram/pool.js +300 -0
- package/dist/src/telegram/pool.js.map +1 -0
- package/dist/src/telegram/session-files.d.ts +4 -0
- package/dist/src/telegram/session-files.js +34 -0
- package/dist/src/telegram/session-files.js.map +1 -0
- package/package.json +62 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export class CapabilityRegistry {
|
|
2
|
+
actions = new Map();
|
|
3
|
+
triggers = new Map();
|
|
4
|
+
services = new Map();
|
|
5
|
+
owners = new Map();
|
|
6
|
+
controlGrants = new Map();
|
|
7
|
+
/** 返回拥有该能力的插件日志作用域(未登记时为 undefined)。 */
|
|
8
|
+
scopeOf(kind, name) {
|
|
9
|
+
return this.owners.get(`${kind}:${name}`);
|
|
10
|
+
}
|
|
11
|
+
/** 该能力是否声明需要运行时控制(未声明时上下文中不注入 RuntimeControl)。 */
|
|
12
|
+
grantsControl(kind, name) {
|
|
13
|
+
return this.controlGrants.get(`${kind}:${name}`) === true;
|
|
14
|
+
}
|
|
15
|
+
register(kind, name, constructor, scope, options) {
|
|
16
|
+
const normalized = name.trim();
|
|
17
|
+
if (!normalized)
|
|
18
|
+
throw new Error(kind + " capability name cannot be empty");
|
|
19
|
+
const target = this.mapFor(kind);
|
|
20
|
+
if (target.has(normalized))
|
|
21
|
+
throw new Error("duplicate capability: " + normalized);
|
|
22
|
+
if (this.hasAny(normalized))
|
|
23
|
+
throw new Error("capability is registered more than once: " + normalized);
|
|
24
|
+
target.set(normalized, constructor);
|
|
25
|
+
if (scope)
|
|
26
|
+
this.owners.set(`${kind}:${normalized}`, scope);
|
|
27
|
+
if (options?.control === true)
|
|
28
|
+
this.controlGrants.set(`${kind}:${normalized}`, true);
|
|
29
|
+
}
|
|
30
|
+
getAction(name) {
|
|
31
|
+
const constructor = this.actions.get(name);
|
|
32
|
+
if (!constructor)
|
|
33
|
+
throw new Error("unknown action capability: " + name);
|
|
34
|
+
return constructor;
|
|
35
|
+
}
|
|
36
|
+
getTrigger(name) {
|
|
37
|
+
const constructor = this.triggers.get(name);
|
|
38
|
+
if (!constructor)
|
|
39
|
+
throw new Error("unknown trigger capability: " + name);
|
|
40
|
+
return constructor;
|
|
41
|
+
}
|
|
42
|
+
getService(name) {
|
|
43
|
+
const constructor = this.services.get(name);
|
|
44
|
+
if (!constructor)
|
|
45
|
+
throw new Error("unknown service capability: " + name);
|
|
46
|
+
return constructor;
|
|
47
|
+
}
|
|
48
|
+
has(name) {
|
|
49
|
+
return this.hasAny(name);
|
|
50
|
+
}
|
|
51
|
+
hasAny(name) {
|
|
52
|
+
return this.actions.has(name) || this.triggers.has(name) || this.services.has(name);
|
|
53
|
+
}
|
|
54
|
+
mapFor(kind) {
|
|
55
|
+
if (kind === "action")
|
|
56
|
+
return this.actions;
|
|
57
|
+
if (kind === "trigger")
|
|
58
|
+
return this.triggers;
|
|
59
|
+
return this.services;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/extensions/registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Logger as GramLogger } from "telegram/extensions/Logger.js";
|
|
2
|
+
import type { RuntimeLogger } from "@paperkite/sdk";
|
|
3
|
+
import type { AppSettings } from "../config/settings.js";
|
|
4
|
+
export interface SessionClient {
|
|
5
|
+
connect(): Promise<unknown>;
|
|
6
|
+
disconnect(): Promise<unknown>;
|
|
7
|
+
session: {
|
|
8
|
+
save(): string;
|
|
9
|
+
};
|
|
10
|
+
invoke(request: unknown): Promise<unknown>;
|
|
11
|
+
start(options: {
|
|
12
|
+
phoneNumber: () => Promise<string>;
|
|
13
|
+
phoneCode: () => Promise<string>;
|
|
14
|
+
password: () => Promise<string>;
|
|
15
|
+
onError: (error: unknown) => void;
|
|
16
|
+
}): Promise<unknown>;
|
|
17
|
+
}
|
|
18
|
+
export declare function createGramClient(sessionName: string, content: string): SessionClient;
|
|
19
|
+
export declare function configureTelegramClientFactory(settings: AppSettings, logger?: RuntimeLogger): void;
|
|
20
|
+
export declare function createGramLogger(logger: RuntimeLogger, level: string): GramLogger;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { TelegramClient } from "telegram";
|
|
3
|
+
import { StringSession } from "telegram/sessions/index.js";
|
|
4
|
+
import { Logger as GramLogger, LogLevel } from "telegram/extensions/Logger.js";
|
|
5
|
+
import { paperkiteHome } from "../config/paths.js";
|
|
6
|
+
export function createGramClient(sessionName, content) {
|
|
7
|
+
const session = new StringSession(content);
|
|
8
|
+
return new TelegramClient(session, currentSettings.telegram.apiId, currentSettings.telegram.apiHash, {
|
|
9
|
+
connectionRetries: 5,
|
|
10
|
+
autoReconnect: true,
|
|
11
|
+
...(currentLogger ? { baseLogger: createGramLogger(currentLogger, currentSettings.logging.level) } : {})
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
let currentSettings = {
|
|
15
|
+
telegram: { apiId: 0, apiHash: "", sessionsDir: join(paperkiteHome(), "accounts") },
|
|
16
|
+
logging: { level: "info", directory: join(paperkiteHome(), "logs") }
|
|
17
|
+
};
|
|
18
|
+
let currentLogger;
|
|
19
|
+
export function configureTelegramClientFactory(settings, logger) {
|
|
20
|
+
currentSettings = settings;
|
|
21
|
+
currentLogger = logger;
|
|
22
|
+
}
|
|
23
|
+
export function createGramLogger(logger, level) {
|
|
24
|
+
const gram = new GramLogger(gramLogLevel(level));
|
|
25
|
+
gram.log = (logLevel, message) => {
|
|
26
|
+
switch (logLevel) {
|
|
27
|
+
case LogLevel.ERROR:
|
|
28
|
+
logger.error(message);
|
|
29
|
+
break;
|
|
30
|
+
case LogLevel.WARN:
|
|
31
|
+
logger.warn(message);
|
|
32
|
+
break;
|
|
33
|
+
case LogLevel.DEBUG:
|
|
34
|
+
logger.debug(message);
|
|
35
|
+
break;
|
|
36
|
+
default:
|
|
37
|
+
logger.info(message);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
return gram;
|
|
41
|
+
}
|
|
42
|
+
function gramLogLevel(level) {
|
|
43
|
+
switch (level.toLowerCase()) {
|
|
44
|
+
case "debug":
|
|
45
|
+
return LogLevel.DEBUG;
|
|
46
|
+
case "warn":
|
|
47
|
+
return LogLevel.WARN;
|
|
48
|
+
case "error":
|
|
49
|
+
return LogLevel.ERROR;
|
|
50
|
+
default:
|
|
51
|
+
return LogLevel.INFO;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/telegram/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAG/E,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAenD,MAAM,UAAU,gBAAgB,CAAC,WAAmB,EAAE,OAAe;IACnE,MAAM,OAAO,GAAG,IAAI,aAAa,CAAC,OAAO,CAAC,CAAC;IAC3C,OAAO,IAAI,cAAc,CAAC,OAAO,EAAE,eAAe,CAAC,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC,QAAQ,CAAC,OAAO,EAAE;QACnG,iBAAiB,EAAE,CAAC;QACpB,aAAa,EAAE,IAAI;QACnB,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,gBAAgB,CAAC,aAAa,EAAE,eAAe,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACzG,CAA6B,CAAC;AACjC,CAAC;AAED,IAAI,eAAe,GAAgB;IACjC,QAAQ,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,WAAW,EAAE,IAAI,CAAC,aAAa,EAAE,EAAE,UAAU,CAAC,EAAE;IACnF,OAAO,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,aAAa,EAAE,EAAE,MAAM,CAAC,EAAE;CACrE,CAAC;AAEF,IAAI,aAAwC,CAAC;AAE7C,MAAM,UAAU,8BAA8B,CAAC,QAAqB,EAAE,MAAsB;IAC1F,eAAe,GAAG,QAAQ,CAAC;IAC3B,aAAa,GAAG,MAAM,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,MAAqB,EAAE,KAAa;IACnE,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;IACjD,IAAI,CAAC,GAAG,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE;QAC/B,QAAQ,QAAQ,EAAE,CAAC;YACjB,KAAK,QAAQ,CAAC,KAAK;gBACjB,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;gBACtB,MAAM;YACR,KAAK,QAAQ,CAAC,IAAI;gBAChB,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;gBACrB,MAAM;YACR,KAAK,QAAQ,CAAC,KAAK;gBACjB,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;gBACtB,MAAM;YACR;gBACE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACzB,CAAC;IACH,CAAC,CAAC;IACF,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,YAAY,CAAC,KAAa;IACjC,QAAQ,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;QAC5B,KAAK,OAAO;YACV,OAAO,QAAQ,CAAC,KAAK,CAAC;QACxB,KAAK,MAAM;YACT,OAAO,QAAQ,CAAC,IAAI,CAAC;QACvB,KAAK,OAAO;YACV,OAAO,QAAQ,CAAC,KAAK,CAAC;QACxB;YACE,OAAO,QAAQ,CAAC,IAAI,CAAC;IACzB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { isSessionUnavailable } from "@paperkite/sdk";
|
|
2
|
+
const AUTH_NAMES = [
|
|
3
|
+
"AUTH_KEY_UNREGISTERED",
|
|
4
|
+
"AUTH_KEY_INVALID",
|
|
5
|
+
"AUTH_KEY_DUPLICATED",
|
|
6
|
+
"AUTH_KEY_PERM_EMPTY",
|
|
7
|
+
"AUTH_KEY_BANNED",
|
|
8
|
+
"AUTH_KEY_NOT_FOUND",
|
|
9
|
+
"AUTH_RESTART",
|
|
10
|
+
"RESTART_AUTH",
|
|
11
|
+
"SESSION_REVOKED",
|
|
12
|
+
"SESSION_EXPIRED",
|
|
13
|
+
"SESSION_PASSWORD_NEEDED",
|
|
14
|
+
"ACTIVE_USER_LIMIT",
|
|
15
|
+
"USER_DEACTIVATED_BAN",
|
|
16
|
+
"USER_DEACTIVATED",
|
|
17
|
+
"PHONE_CODE_INVALID",
|
|
18
|
+
"PHONE_CODE_EXPIRED",
|
|
19
|
+
"PHONE_NUMBER_INVALID",
|
|
20
|
+
"PHONE_NUMBER_BANNED"
|
|
21
|
+
];
|
|
22
|
+
const ACCOUNT_NAMES = [
|
|
23
|
+
"FLOOD_WAIT",
|
|
24
|
+
"FLOOD_PREMIUM_WAIT",
|
|
25
|
+
"FLOOD",
|
|
26
|
+
"MSG_WAIT",
|
|
27
|
+
"CONNECTION_NOT_INITED",
|
|
28
|
+
"NETWORK_MIGRATE",
|
|
29
|
+
"USER_MIGRATE",
|
|
30
|
+
"PHONE_MIGRATE",
|
|
31
|
+
"FILE_MIGRATE",
|
|
32
|
+
"INTERNAL"
|
|
33
|
+
];
|
|
34
|
+
const ACTION_NAMES = ["SLOWMODE_WAIT", "EMAIL_UNCONFIRMED"];
|
|
35
|
+
const NETWORK_PHRASES = [
|
|
36
|
+
"CONNECTION CLOSED",
|
|
37
|
+
"CONNECTION LOST",
|
|
38
|
+
"CONNECTION REFUSED",
|
|
39
|
+
"CONNECTION ERROR",
|
|
40
|
+
"SOCKET CLOSED",
|
|
41
|
+
"SOCKET ERROR",
|
|
42
|
+
"UNABLE TO CONNECT",
|
|
43
|
+
"CANNOT CONNECT",
|
|
44
|
+
"RECONNECT",
|
|
45
|
+
"UNEXPECTED DATA CENTER",
|
|
46
|
+
"ECONNRESET",
|
|
47
|
+
"ECONNREFUSED",
|
|
48
|
+
"ENOTFOUND",
|
|
49
|
+
"EPIPE",
|
|
50
|
+
"TIMED OUT",
|
|
51
|
+
"REQUEST TIMEOUT",
|
|
52
|
+
"NETWORK ERROR"
|
|
53
|
+
];
|
|
54
|
+
export function classifySessionFailure(error) {
|
|
55
|
+
if (isSessionUnavailable(error))
|
|
56
|
+
return "none";
|
|
57
|
+
const fields = rpcFields(error);
|
|
58
|
+
if (fields)
|
|
59
|
+
return classifyRpc(fields.name, fields.code);
|
|
60
|
+
const text = String(error instanceof Error ? error.message : error).toUpperCase();
|
|
61
|
+
if (containsAny(text, AUTH_NAMES))
|
|
62
|
+
return "auth";
|
|
63
|
+
if (containsAny(text, NETWORK_PHRASES))
|
|
64
|
+
return "account";
|
|
65
|
+
return "none";
|
|
66
|
+
}
|
|
67
|
+
function classifyRpc(name, code) {
|
|
68
|
+
if (containsAny(name, AUTH_NAMES))
|
|
69
|
+
return "auth";
|
|
70
|
+
if (containsAny(name, ACCOUNT_NAMES))
|
|
71
|
+
return "account";
|
|
72
|
+
if (containsAny(name, ACTION_NAMES))
|
|
73
|
+
return "action";
|
|
74
|
+
const absolute = code === undefined ? 0 : Math.abs(code);
|
|
75
|
+
if (absolute === 401 || absolute === 406)
|
|
76
|
+
return "auth";
|
|
77
|
+
if (absolute === 420 || (absolute >= 500 && absolute <= 599))
|
|
78
|
+
return "account";
|
|
79
|
+
if (absolute === 400 || absolute === 403 || absolute === 404 || absolute === 409 || absolute === 303) {
|
|
80
|
+
return "action";
|
|
81
|
+
}
|
|
82
|
+
return "none";
|
|
83
|
+
}
|
|
84
|
+
function rpcFields(error) {
|
|
85
|
+
if (typeof error !== "object" || error === null)
|
|
86
|
+
return undefined;
|
|
87
|
+
const candidate = error;
|
|
88
|
+
if (typeof candidate.errorMessage !== "string")
|
|
89
|
+
return undefined;
|
|
90
|
+
return {
|
|
91
|
+
name: candidate.errorMessage.toUpperCase(),
|
|
92
|
+
code: typeof candidate.code === "number" ? candidate.code : undefined
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function containsAny(text, names) {
|
|
96
|
+
return names.some((name) => text.includes(name));
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=failure.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"failure.js","sourceRoot":"","sources":["../../../src/telegram/failure.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function loginSession(name: string, settingsFile: string): Promise<void>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { createInterface } from "node:readline/promises";
|
|
2
|
+
import { stdin as input, stdout as output } from "node:process";
|
|
3
|
+
import { loadSettings } from "../config/settings.js";
|
|
4
|
+
import { AppLogger } from "../engine/logger.js";
|
|
5
|
+
import { configureTelegramClientFactory, createGramClient } from "./client.js";
|
|
6
|
+
import { normalizeSessionName, sessionFilePath, writeSessionFile } from "./session-files.js";
|
|
7
|
+
export async function loginSession(name, settingsFile) {
|
|
8
|
+
if (!input.isTTY)
|
|
9
|
+
throw new Error("session login needs an interactive terminal");
|
|
10
|
+
const settings = await loadSettings(settingsFile);
|
|
11
|
+
const session = normalizeSessionName(name);
|
|
12
|
+
sessionFilePath(settings.telegram.sessionsDir, session);
|
|
13
|
+
const logger = new AppLogger(settings.logging.level);
|
|
14
|
+
configureTelegramClientFactory(settings, logger);
|
|
15
|
+
const client = createGramClient(session, "");
|
|
16
|
+
const readline = createInterface({ input, output });
|
|
17
|
+
try {
|
|
18
|
+
await client.start({
|
|
19
|
+
phoneNumber: () => readline.question("Phone number: "),
|
|
20
|
+
phoneCode: () => readline.question("Login code: "),
|
|
21
|
+
password: () => readline.question("Two-factor password: "),
|
|
22
|
+
onError: (error) => logger.warn("session login step failed: " + session, error)
|
|
23
|
+
});
|
|
24
|
+
await writeSessionFile(settings.telegram.sessionsDir, session, client.session.save());
|
|
25
|
+
}
|
|
26
|
+
finally {
|
|
27
|
+
readline.close();
|
|
28
|
+
await client.disconnect().catch(() => undefined);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=login.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../../src/telegram/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,KAAK,IAAI,KAAK,EAAE,MAAM,IAAI,MAAM,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,8BAA8B,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/E,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE7F,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAY,EAAE,YAAoB;IACnE,IAAI,CAAC,KAAK,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;IACjF,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,YAAY,CAAC,CAAC;IAClD,MAAM,OAAO,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC;IAC3C,eAAe,CAAC,QAAQ,CAAC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IACxD,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACrD,8BAA8B,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACjD,MAAM,MAAM,GAAG,gBAAgB,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC7C,MAAM,QAAQ,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IACpD,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,KAAK,CAAC;YACjB,WAAW,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,gBAAgB,CAAC;YACtD,SAAS,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,cAAc,CAAC;YAClD,QAAQ,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,uBAAuB,CAAC;YAC1D,OAAO,EAAE,CAAC,KAAc,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,6BAA6B,GAAG,OAAO,EAAE,KAAK,CAAC;SACzF,CAAC,CAAC;QACH,MAAM,gBAAgB,CAAC,QAAQ,CAAC,QAAQ,CAAC,WAAW,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IACxF,CAAC;YAAS,CAAC;QACT,QAAQ,CAAC,KAAK,EAAE,CAAC;QACjB,MAAM,MAAM,CAAC,UAAU,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IACnD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { type RuntimeLogger, type SessionAccess, type SessionState, type Unsubscribe } from "@paperkite/sdk";
|
|
2
|
+
import type { AppSettings } from "../config/settings.js";
|
|
3
|
+
import { type SessionClient } from "./client.js";
|
|
4
|
+
export interface SessionStateInfo {
|
|
5
|
+
readonly name: string;
|
|
6
|
+
readonly state: SessionState;
|
|
7
|
+
readonly since: number;
|
|
8
|
+
readonly reason?: string;
|
|
9
|
+
readonly attempts: number;
|
|
10
|
+
}
|
|
11
|
+
export interface SessionStateChange {
|
|
12
|
+
readonly name: string;
|
|
13
|
+
readonly previous: SessionState;
|
|
14
|
+
readonly state: SessionState;
|
|
15
|
+
readonly reason?: string;
|
|
16
|
+
}
|
|
17
|
+
export type SessionStateListener = (change: SessionStateChange) => void;
|
|
18
|
+
export declare class SessionPool {
|
|
19
|
+
private readonly settings;
|
|
20
|
+
private readonly logger;
|
|
21
|
+
private readonly createClient;
|
|
22
|
+
private readonly entries;
|
|
23
|
+
private readonly listeners;
|
|
24
|
+
private readonly guard;
|
|
25
|
+
private closed;
|
|
26
|
+
constructor(settings: AppSettings, logger: RuntimeLogger, createClient?: (session: string, content: string) => SessionClient);
|
|
27
|
+
subscribe(listener: SessionStateListener): Unsubscribe;
|
|
28
|
+
ensure(names: Iterable<string>): Promise<void>;
|
|
29
|
+
access(name?: string): SessionAccess | undefined;
|
|
30
|
+
state(name: string): SessionState | undefined;
|
|
31
|
+
states(): SessionStateInfo[];
|
|
32
|
+
run<T>(name: string, operation: (client: SessionClient) => T | Promise<T>): Promise<T>;
|
|
33
|
+
reconnectSession(name: string): Promise<void>;
|
|
34
|
+
closeAll(): Promise<void>;
|
|
35
|
+
private createEntry;
|
|
36
|
+
private attempt;
|
|
37
|
+
private performAttempt;
|
|
38
|
+
private failAttempt;
|
|
39
|
+
private scheduleBackoff;
|
|
40
|
+
private noteFailure;
|
|
41
|
+
private noteSuccess;
|
|
42
|
+
private transition;
|
|
43
|
+
private unavailable;
|
|
44
|
+
private disposeClient;
|
|
45
|
+
private saveSession;
|
|
46
|
+
private checkAuthorized;
|
|
47
|
+
}
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { Api } from "telegram/tl/api.js";
|
|
2
|
+
import { SessionUnavailableError } from "@paperkite/sdk";
|
|
3
|
+
import { createGramClient } from "./client.js";
|
|
4
|
+
import { classifySessionFailure } from "./failure.js";
|
|
5
|
+
import { normalizeSessionName, readSessionFile, writeSessionFile } from "./session-files.js";
|
|
6
|
+
const DEFAULT_GUARD = { windowMs: 60_000, threshold: 5, backoffMinMs: 30_000, backoffMaxMs: 30 * 60_000 };
|
|
7
|
+
export class SessionPool {
|
|
8
|
+
settings;
|
|
9
|
+
logger;
|
|
10
|
+
createClient;
|
|
11
|
+
entries = new Map();
|
|
12
|
+
listeners = new Set();
|
|
13
|
+
guard;
|
|
14
|
+
closed = false;
|
|
15
|
+
constructor(settings, logger, createClient = createGramClient) {
|
|
16
|
+
this.settings = settings;
|
|
17
|
+
this.logger = logger;
|
|
18
|
+
this.createClient = createClient;
|
|
19
|
+
const configured = settings.telegram.sessionGuard;
|
|
20
|
+
this.guard = configured
|
|
21
|
+
? {
|
|
22
|
+
windowMs: configured.windowMs,
|
|
23
|
+
threshold: configured.threshold,
|
|
24
|
+
backoffMinMs: configured.backoffMinMs,
|
|
25
|
+
backoffMaxMs: configured.backoffMaxMs
|
|
26
|
+
}
|
|
27
|
+
: DEFAULT_GUARD;
|
|
28
|
+
}
|
|
29
|
+
subscribe(listener) {
|
|
30
|
+
this.listeners.add(listener);
|
|
31
|
+
return () => this.listeners.delete(listener);
|
|
32
|
+
}
|
|
33
|
+
async ensure(names) {
|
|
34
|
+
const pending = [];
|
|
35
|
+
for (const name of new Set([...names].map((value) => normalizeSessionName(value)))) {
|
|
36
|
+
const existing = this.entries.get(name);
|
|
37
|
+
if (!existing) {
|
|
38
|
+
const created = this.createEntry(name);
|
|
39
|
+
void this.attempt(created);
|
|
40
|
+
pending.push(created);
|
|
41
|
+
}
|
|
42
|
+
else if (existing.health.state === "starting") {
|
|
43
|
+
pending.push(existing);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
await Promise.all(pending.map((entry) => entry.ready));
|
|
47
|
+
}
|
|
48
|
+
access(name) {
|
|
49
|
+
if (name === undefined)
|
|
50
|
+
return undefined;
|
|
51
|
+
return { run: (operation) => this.run(name, operation) };
|
|
52
|
+
}
|
|
53
|
+
state(name) {
|
|
54
|
+
return this.entries.get(normalizeSessionName(name))?.health.state;
|
|
55
|
+
}
|
|
56
|
+
states() {
|
|
57
|
+
return [...this.entries.values()].map((entry) => ({
|
|
58
|
+
name: entry.name,
|
|
59
|
+
state: entry.health.state,
|
|
60
|
+
since: entry.health.since,
|
|
61
|
+
reason: entry.health.reason,
|
|
62
|
+
attempts: entry.health.attempts
|
|
63
|
+
}));
|
|
64
|
+
}
|
|
65
|
+
async run(name, operation) {
|
|
66
|
+
const entry = this.entries.get(normalizeSessionName(name));
|
|
67
|
+
if (!entry)
|
|
68
|
+
throw new Error("session is not initialized: " + name);
|
|
69
|
+
if (entry.health.state === "starting")
|
|
70
|
+
await entry.ready;
|
|
71
|
+
if (entry.health.state !== "connected")
|
|
72
|
+
throw this.unavailable(entry);
|
|
73
|
+
const previous = entry.tail;
|
|
74
|
+
let resolveTail;
|
|
75
|
+
entry.tail = new Promise((resolvePromise) => {
|
|
76
|
+
resolveTail = resolvePromise;
|
|
77
|
+
});
|
|
78
|
+
await previous;
|
|
79
|
+
if (entry.health.state !== "connected" || !entry.client) {
|
|
80
|
+
resolveTail?.();
|
|
81
|
+
throw this.unavailable(entry);
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
const value = await operation(entry.client);
|
|
85
|
+
this.noteSuccess(entry);
|
|
86
|
+
return value;
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
this.noteFailure(entry, error);
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
finally {
|
|
93
|
+
resolveTail?.();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
async reconnectSession(name) {
|
|
97
|
+
const entry = this.entries.get(normalizeSessionName(name));
|
|
98
|
+
if (!entry)
|
|
99
|
+
throw new Error("unknown session: " + name);
|
|
100
|
+
if (entry.health.state === "starting" || entry.health.state === "connected")
|
|
101
|
+
return;
|
|
102
|
+
await this.attempt(entry);
|
|
103
|
+
}
|
|
104
|
+
async closeAll() {
|
|
105
|
+
if (this.closed)
|
|
106
|
+
return;
|
|
107
|
+
this.closed = true;
|
|
108
|
+
const entries = [...this.entries.values()];
|
|
109
|
+
this.entries.clear();
|
|
110
|
+
for (const entry of entries) {
|
|
111
|
+
if (entry.timer)
|
|
112
|
+
clearTimeout(entry.timer);
|
|
113
|
+
await entry.ready.catch(() => undefined);
|
|
114
|
+
await entry.tail;
|
|
115
|
+
await this.disposeClient(entry);
|
|
116
|
+
}
|
|
117
|
+
this.listeners.clear();
|
|
118
|
+
}
|
|
119
|
+
createEntry(name) {
|
|
120
|
+
const health = {
|
|
121
|
+
state: "isolated",
|
|
122
|
+
since: Date.now(),
|
|
123
|
+
reason: "starting",
|
|
124
|
+
attempts: 0,
|
|
125
|
+
faultsWindowStart: Date.now(),
|
|
126
|
+
faults: 0
|
|
127
|
+
};
|
|
128
|
+
const entry = { name, ready: Promise.resolve(), tail: Promise.resolve(), health };
|
|
129
|
+
this.entries.set(name, entry);
|
|
130
|
+
return entry;
|
|
131
|
+
}
|
|
132
|
+
async attempt(entry) {
|
|
133
|
+
if (this.closed)
|
|
134
|
+
return;
|
|
135
|
+
if (entry.health.state === "starting") {
|
|
136
|
+
await entry.ready.catch(() => undefined);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (entry.health.state === "connected")
|
|
140
|
+
return;
|
|
141
|
+
if (entry.timer)
|
|
142
|
+
clearTimeout(entry.timer);
|
|
143
|
+
entry.timer = undefined;
|
|
144
|
+
entry.health.reason = "connecting";
|
|
145
|
+
entry.health.state = "starting";
|
|
146
|
+
entry.ready = new Promise((resolvePromise) => {
|
|
147
|
+
entry.readyResolve = resolvePromise;
|
|
148
|
+
});
|
|
149
|
+
try {
|
|
150
|
+
const outcome = await this.performAttempt(entry);
|
|
151
|
+
if (this.closed)
|
|
152
|
+
return;
|
|
153
|
+
if (outcome.ok) {
|
|
154
|
+
await this.saveSession(entry);
|
|
155
|
+
this.transition(entry, "connected");
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
this.failAttempt(entry, outcome.error, outcome.hadContent);
|
|
159
|
+
}
|
|
160
|
+
finally {
|
|
161
|
+
entry.readyResolve?.();
|
|
162
|
+
entry.readyResolve = undefined;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
async performAttempt(entry) {
|
|
166
|
+
await this.disposeClient(entry);
|
|
167
|
+
const content = await readSessionFile(this.settings.telegram.sessionsDir, entry.name).catch((error) => error instanceof Error ? error : new Error(String(error)));
|
|
168
|
+
if (content instanceof Error)
|
|
169
|
+
return { ok: false, error: content, hadContent: false };
|
|
170
|
+
const hadContent = Boolean(content);
|
|
171
|
+
if (!hadContent) {
|
|
172
|
+
return { ok: false, error: new Error("session " + entry.name + " has no saved login"), hadContent: false };
|
|
173
|
+
}
|
|
174
|
+
try {
|
|
175
|
+
const client = this.createClient(entry.name, content);
|
|
176
|
+
entry.client = client;
|
|
177
|
+
await client.connect();
|
|
178
|
+
await this.checkAuthorized(client);
|
|
179
|
+
return { ok: true, hadContent };
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
return { ok: false, error, hadContent };
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
failAttempt(entry, error, hadContent) {
|
|
186
|
+
const message = messageOf(error);
|
|
187
|
+
const kind = classifySessionFailure(error);
|
|
188
|
+
if (kind === "auth" || !hadContent) {
|
|
189
|
+
this.transition(entry, "waiting-auth", loginHint(entry.name, message));
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
entry.health.attempts += 1;
|
|
193
|
+
this.transition(entry, "isolated", message);
|
|
194
|
+
this.scheduleBackoff(entry);
|
|
195
|
+
}
|
|
196
|
+
scheduleBackoff(entry) {
|
|
197
|
+
if (this.closed || entry.health.state !== "isolated")
|
|
198
|
+
return;
|
|
199
|
+
const delay = Math.min(this.guard.backoffMaxMs, this.guard.backoffMinMs * 2 ** Math.max(0, entry.health.attempts - 1));
|
|
200
|
+
const timer = setTimeout(() => void this.attempt(entry), delay);
|
|
201
|
+
timer.unref();
|
|
202
|
+
entry.timer = timer;
|
|
203
|
+
}
|
|
204
|
+
noteFailure(entry, error) {
|
|
205
|
+
const kind = classifySessionFailure(error);
|
|
206
|
+
if (kind === "auth") {
|
|
207
|
+
this.transition(entry, "waiting-auth", loginHint(entry.name, messageOf(error)));
|
|
208
|
+
void this.disposeClient(entry);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
if (kind !== "account")
|
|
212
|
+
return;
|
|
213
|
+
const now = Date.now();
|
|
214
|
+
if (now - entry.health.faultsWindowStart > this.guard.windowMs) {
|
|
215
|
+
entry.health.faultsWindowStart = now;
|
|
216
|
+
entry.health.faults = 0;
|
|
217
|
+
}
|
|
218
|
+
entry.health.faults += 1;
|
|
219
|
+
if (entry.health.faults < this.guard.threshold)
|
|
220
|
+
return;
|
|
221
|
+
this.transition(entry, "isolated", `${entry.health.faults} session failures within ${this.guard.windowMs / 1_000}s`);
|
|
222
|
+
void this.disposeClient(entry);
|
|
223
|
+
entry.health.attempts = 0;
|
|
224
|
+
this.scheduleBackoff(entry);
|
|
225
|
+
}
|
|
226
|
+
noteSuccess(entry) {
|
|
227
|
+
entry.health.faults = 0;
|
|
228
|
+
entry.health.faultsWindowStart = Date.now();
|
|
229
|
+
}
|
|
230
|
+
transition(entry, state, reason) {
|
|
231
|
+
if (entry.health.state === state) {
|
|
232
|
+
if (state === "connected")
|
|
233
|
+
entry.health.reason = undefined;
|
|
234
|
+
else if (reason !== undefined)
|
|
235
|
+
entry.health.reason = reason;
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
const previous = entry.health.state;
|
|
239
|
+
entry.health.state = state;
|
|
240
|
+
entry.health.since = Date.now();
|
|
241
|
+
if (state === "connected")
|
|
242
|
+
entry.health.reason = undefined;
|
|
243
|
+
else if (reason !== undefined)
|
|
244
|
+
entry.health.reason = reason;
|
|
245
|
+
if (state === "connected" || state === "waiting-auth")
|
|
246
|
+
entry.health.attempts = 0;
|
|
247
|
+
if (this.closed)
|
|
248
|
+
return;
|
|
249
|
+
this.logger.info(`session ${entry.name} ${previous} -> ${state}` + (reason ? ": " + reason : ""));
|
|
250
|
+
for (const listener of [...this.listeners]) {
|
|
251
|
+
try {
|
|
252
|
+
listener({ name: entry.name, previous, state, reason });
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
this.logger.error("session state listener failed", error);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
unavailable(entry) {
|
|
260
|
+
return new SessionUnavailableError(entry.name, entry.health.state, entry.health.reason);
|
|
261
|
+
}
|
|
262
|
+
async disposeClient(entry) {
|
|
263
|
+
const client = entry.client;
|
|
264
|
+
entry.client = undefined;
|
|
265
|
+
if (!client)
|
|
266
|
+
return;
|
|
267
|
+
try {
|
|
268
|
+
const saved = client.session.save();
|
|
269
|
+
if (saved)
|
|
270
|
+
await writeSessionFile(this.settings.telegram.sessionsDir, entry.name, saved);
|
|
271
|
+
}
|
|
272
|
+
catch (error) {
|
|
273
|
+
this.logger.warn("could not save session " + entry.name, error);
|
|
274
|
+
}
|
|
275
|
+
await client.disconnect().catch((error) => {
|
|
276
|
+
this.logger.warn("could not disconnect session " + entry.name, error);
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
async saveSession(entry) {
|
|
280
|
+
if (!entry.client)
|
|
281
|
+
return;
|
|
282
|
+
try {
|
|
283
|
+
await writeSessionFile(this.settings.telegram.sessionsDir, entry.name, entry.client.session.save());
|
|
284
|
+
}
|
|
285
|
+
catch (error) {
|
|
286
|
+
this.logger.warn("could not save session " + entry.name, error);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
async checkAuthorized(client) {
|
|
290
|
+
await client.invoke(new Api.updates.GetState());
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
function messageOf(error) {
|
|
294
|
+
return error instanceof Error ? error.message : String(error);
|
|
295
|
+
}
|
|
296
|
+
function loginHint(name, detail) {
|
|
297
|
+
const base = detail && detail.trim() ? detail : "session login is required";
|
|
298
|
+
return base + "; run `paperkite session login " + name + "` to log in";
|
|
299
|
+
}
|
|
300
|
+
//# sourceMappingURL=pool.js.map
|