tsarr 2.7.6 → 2.8.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 +4 -1
- package/dist/cli/commands/config.d.ts.map +1 -1
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/service.d.ts.map +1 -1
- package/dist/cli/completions.d.ts.map +1 -1
- package/dist/cli/config.d.ts +8 -2
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/index.js +314 -135
- package/dist/tsarr-2.8.0.tgz +0 -0
- package/package.json +2 -2
- package/dist/tsarr-2.7.6.tgz +0 -0
package/README.md
CHANGED
|
@@ -157,7 +157,7 @@ export TSARR_RADARR_URL=http://localhost:7878
|
|
|
157
157
|
export TSARR_RADARR_API_KEY=your-api-key
|
|
158
158
|
```
|
|
159
159
|
|
|
160
|
-
Config is stored in `~/.config/tsarr/config.json` (global) or `.tsarr.json` (local project). Environment variables take priority over config files.
|
|
160
|
+
Config is stored in `~/.config/tsarr/config.json` (global) or `.tsarr.json` (local project). Environment variables take priority over config files. You can configure multiple instances of the same service (e.g. a 4K and 1080p Radarr) — see the [CLI Guide](./docs/cli.md) for details.
|
|
161
161
|
|
|
162
162
|
### Usage
|
|
163
163
|
|
|
@@ -171,6 +171,9 @@ tsarr sonarr series list
|
|
|
171
171
|
tsarr prowlarr indexer list
|
|
172
172
|
tsarr lidarr artist search --term "Radiohead"
|
|
173
173
|
|
|
174
|
+
# Multi-instance: target a specific named instance
|
|
175
|
+
tsarr radarr movie list --instance 4K
|
|
176
|
+
|
|
174
177
|
# Output formats
|
|
175
178
|
tsarr radarr movie list --table # Table (default in terminal)
|
|
176
179
|
tsarr radarr movie list --json # JSON (default when piped)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/config.ts"],"names":[],"mappings":"AAmPA,eAAO,MAAM,MAAM,qDAWjB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/doctor.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/doctor.ts"],"names":[],"mappings":"AAqCA,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;EAwGjB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/service.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;IACvC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,SAAS,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,GAAG,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;CAC/D;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,SAAS,EAAE,CAAC;CACtB;AAED,eAAO,MAAM,sBAAsB,UAA0D,CAAC;AAE9F,wBAAgB,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,CAAC,EAAE,CAM5E;AAED,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,GAAG,EACnC,SAAS,EAAE,WAAW,EAAE,
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/service.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;IACvC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,SAAS,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,GAAG,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;CAC/D;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,SAAS,EAAE,CAAC;CACtB;AAED,eAAO,MAAM,sBAAsB,UAA0D,CAAC;AAE9F,wBAAgB,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,CAAC,EAAE,CAM5E;AAED,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,GAAG,EACnC,SAAS,EAAE,WAAW,EAAE,uDAoNzB;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,CAE5C;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,GAAG,CAGnD;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAS1E;AAED,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAMlF;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAMpF;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,GAAG,GAAG,MAAM,GAAG,SAAS,CAE5D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"completions.d.ts","sourceRoot":"","sources":["../../src/cli/completions.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"completions.d.ts","sourceRoot":"","sources":["../../src/cli/completions.ts"],"names":[],"mappings":"AAqOA,eAAO,MAAM,WAAW;;;;;;EA4BtB,CAAC"}
|
package/dist/cli/config.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { QBittorrentClientConfig, ServarrClientConfig } from '../core/types';
|
|
2
2
|
export interface ServiceConfig {
|
|
3
|
+
name?: string;
|
|
3
4
|
baseUrl: string;
|
|
4
5
|
apiKey?: string;
|
|
5
6
|
apiKeyFile?: string;
|
|
@@ -7,8 +8,9 @@ export interface ServiceConfig {
|
|
|
7
8
|
password?: string;
|
|
8
9
|
timeout?: number;
|
|
9
10
|
}
|
|
11
|
+
/** Internal config — services always normalized to arrays */
|
|
10
12
|
export interface TsarrCliConfig {
|
|
11
|
-
services: Record<string, ServiceConfig>;
|
|
13
|
+
services: Record<string, ServiceConfig[]>;
|
|
12
14
|
defaults?: {
|
|
13
15
|
output?: 'json' | 'table' | 'quiet';
|
|
14
16
|
};
|
|
@@ -17,7 +19,11 @@ declare const SERVICES: readonly ["radarr", "sonarr", "lidarr", "readarr", "prow
|
|
|
17
19
|
declare const GLOBAL_CONFIG_PATH: string;
|
|
18
20
|
declare const LOCAL_CONFIG_NAME = ".tsarr.json";
|
|
19
21
|
export declare function loadConfig(): TsarrCliConfig;
|
|
20
|
-
|
|
22
|
+
/** Returns all instances for a service (normalized, always an array). */
|
|
23
|
+
export declare function getServiceInstances(serviceName: string): ServiceConfig[];
|
|
24
|
+
/** Returns instance names for a service. */
|
|
25
|
+
export declare function getInstanceNames(serviceName: string): string[];
|
|
26
|
+
export declare function getServiceConfig(serviceName: string, instanceName?: string): ServarrClientConfig | QBittorrentClientConfig | null;
|
|
21
27
|
export declare function saveGlobalConfig(config: TsarrCliConfig): void;
|
|
22
28
|
export declare function saveLocalConfig(config: TsarrCliConfig): void;
|
|
23
29
|
export declare function getConfigValue(key: string): string | undefined;
|
package/dist/cli/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/cli/config.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAElF,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/cli/config.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAElF,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,6DAA6D;AAC7D,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,EAAE,CAAC,CAAC;IAC1C,QAAQ,CAAC,EAAE;QACT,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;KACrC,CAAC;CACH;AAUD,QAAA,MAAM,QAAQ,kGASJ,CAAC;AAEX,QAAA,MAAM,kBAAkB,QAAyC,CAAC;AAClE,QAAA,MAAM,iBAAiB,gBAAgB,CAAC;AA6FxC,wBAAgB,UAAU,IAAI,cAAc,CAqE3C;AAoDD,yEAAyE;AACzE,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,EAAE,CAGxE;AAED,4CAA4C;AAC5C,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAI9D;AAWD,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,MAAM,EACnB,YAAY,CAAC,EAAE,MAAM,GACpB,mBAAmB,GAAG,uBAAuB,GAAG,IAAI,CAgCtD;AAiBD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAG7D;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAI5D;AAYD,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAoB9D;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,UAAO,GAAG,IAAI,CAmC9E;AAcD,wBAAgB,qBAAqB,IAAI,MAAM,EAAE,CAWhD;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,GAAG,OAAO,CAAC,cAAc,CAAC,CAMnF;AAED,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,QAAQ,EAAE,CAAC"}
|
package/dist/cli/index.js
CHANGED
|
@@ -4856,6 +4856,7 @@ function normalizeServiceConfig(service) {
|
|
|
4856
4856
|
const normalized = {
|
|
4857
4857
|
baseUrl: service.baseUrl ?? "",
|
|
4858
4858
|
apiKey: service.apiKey ?? "",
|
|
4859
|
+
...service.name ? { name: service.name } : {},
|
|
4859
4860
|
...service.apiKeyFile ? { apiKeyFile: service.apiKeyFile } : {},
|
|
4860
4861
|
...service.username ? { username: service.username } : {},
|
|
4861
4862
|
...service.password ? { password: service.password } : {}
|
|
@@ -4866,12 +4867,24 @@ function normalizeServiceConfig(service) {
|
|
|
4866
4867
|
}
|
|
4867
4868
|
return normalized;
|
|
4868
4869
|
}
|
|
4870
|
+
function normalizeServiceEntry(entry) {
|
|
4871
|
+
if (Array.isArray(entry)) {
|
|
4872
|
+
return entry.map((item) => normalizeServiceConfig(item)).filter((item) => item != null);
|
|
4873
|
+
}
|
|
4874
|
+
const normalized = normalizeServiceConfig(entry);
|
|
4875
|
+
return normalized ? [normalized] : [];
|
|
4876
|
+
}
|
|
4869
4877
|
function normalizeConfig(config) {
|
|
4870
|
-
const services = Object.fromEntries(Object.entries(config.services ?? {}).map(([name,
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
};
|
|
4878
|
+
const services = Object.fromEntries(Object.entries(config.services ?? {}).map(([name, entry]) => [
|
|
4879
|
+
name,
|
|
4880
|
+
normalizeServiceEntry(entry)
|
|
4881
|
+
]).filter(([, instances]) => instances.length > 0));
|
|
4882
|
+
const result = {};
|
|
4883
|
+
if (Object.keys(services).length > 0)
|
|
4884
|
+
result.services = services;
|
|
4885
|
+
if (config.defaults)
|
|
4886
|
+
result.defaults = config.defaults;
|
|
4887
|
+
return result;
|
|
4875
4888
|
}
|
|
4876
4889
|
function readJsonFile(path) {
|
|
4877
4890
|
if (!existsSync(path))
|
|
@@ -4902,7 +4915,7 @@ function getEnvConfig() {
|
|
|
4902
4915
|
if (timeout)
|
|
4903
4916
|
partial.timeout = Number(timeout);
|
|
4904
4917
|
if (Object.keys(partial).length > 0) {
|
|
4905
|
-
services[service] = partial;
|
|
4918
|
+
services[service] = [partial];
|
|
4906
4919
|
}
|
|
4907
4920
|
}
|
|
4908
4921
|
return Object.keys(services).length ? { services } : {};
|
|
@@ -4919,29 +4932,64 @@ function findLocalConfigPath() {
|
|
|
4919
4932
|
dir = parent;
|
|
4920
4933
|
}
|
|
4921
4934
|
}
|
|
4935
|
+
function isArrayEntry(raw) {
|
|
4936
|
+
return Array.isArray(raw);
|
|
4937
|
+
}
|
|
4922
4938
|
function loadConfig() {
|
|
4923
|
-
const
|
|
4939
|
+
const globalRaw = readJsonFile(GLOBAL_CONFIG_PATH);
|
|
4924
4940
|
const localPath = findLocalConfigPath();
|
|
4925
|
-
const
|
|
4941
|
+
const localRaw = localPath ? readJsonFile(localPath) : {};
|
|
4926
4942
|
const env2 = getEnvConfig();
|
|
4943
|
+
let globalDiskRaw = {};
|
|
4944
|
+
let localDiskRaw = {};
|
|
4945
|
+
try {
|
|
4946
|
+
if (existsSync(GLOBAL_CONFIG_PATH)) {
|
|
4947
|
+
globalDiskRaw = JSON.parse(readFileSync(GLOBAL_CONFIG_PATH, "utf-8"));
|
|
4948
|
+
}
|
|
4949
|
+
} catch {}
|
|
4950
|
+
try {
|
|
4951
|
+
if (localPath && existsSync(localPath)) {
|
|
4952
|
+
localDiskRaw = JSON.parse(readFileSync(localPath, "utf-8"));
|
|
4953
|
+
}
|
|
4954
|
+
} catch {}
|
|
4927
4955
|
const allServiceNames = new Set([
|
|
4928
|
-
...Object.keys(
|
|
4929
|
-
...Object.keys(
|
|
4956
|
+
...Object.keys(globalRaw.services ?? {}),
|
|
4957
|
+
...Object.keys(localRaw.services ?? {}),
|
|
4930
4958
|
...Object.keys(env2.services ?? {})
|
|
4931
4959
|
]);
|
|
4932
4960
|
const services = {};
|
|
4933
4961
|
for (const name of allServiceNames) {
|
|
4934
|
-
services[name]
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
|
|
4962
|
+
const globalInstances = globalRaw.services?.[name] ?? [];
|
|
4963
|
+
const localInstances = localRaw.services?.[name] ?? [];
|
|
4964
|
+
const envInstances = env2.services?.[name] ?? [];
|
|
4965
|
+
const globalIsArray = isArrayEntry(globalDiskRaw.services?.[name]);
|
|
4966
|
+
const localIsArray = isArrayEntry(localDiskRaw.services?.[name]);
|
|
4967
|
+
if (globalIsArray || localIsArray) {
|
|
4968
|
+
const base = localInstances.length > 0 ? localInstances : globalInstances;
|
|
4969
|
+
if (envInstances.length > 0 && base.length > 0) {
|
|
4970
|
+
base[0] = { ...base[0], ...envInstances[0] };
|
|
4971
|
+
} else if (envInstances.length > 0) {
|
|
4972
|
+
services[name] = envInstances;
|
|
4973
|
+
continue;
|
|
4974
|
+
}
|
|
4975
|
+
services[name] = base;
|
|
4976
|
+
} else {
|
|
4977
|
+
const globalObj = globalInstances[0];
|
|
4978
|
+
const localObj = localInstances[0];
|
|
4979
|
+
const envObj = envInstances[0];
|
|
4980
|
+
const merged2 = {
|
|
4981
|
+
...globalObj,
|
|
4982
|
+
...localObj,
|
|
4983
|
+
...envObj
|
|
4984
|
+
};
|
|
4985
|
+
services[name] = [merged2];
|
|
4986
|
+
}
|
|
4939
4987
|
}
|
|
4940
4988
|
const merged = {
|
|
4941
4989
|
services,
|
|
4942
4990
|
defaults: {
|
|
4943
|
-
...
|
|
4944
|
-
...
|
|
4991
|
+
...globalRaw.defaults,
|
|
4992
|
+
...localRaw.defaults
|
|
4945
4993
|
}
|
|
4946
4994
|
};
|
|
4947
4995
|
return merged;
|
|
@@ -4952,16 +5000,20 @@ function resolveConfigRelativePath(filePath, configPath) {
|
|
|
4952
5000
|
}
|
|
4953
5001
|
return resolve(dirname(configPath), filePath);
|
|
4954
5002
|
}
|
|
4955
|
-
function getResolvedApiKeyFilePath(serviceName,
|
|
4956
|
-
if (!
|
|
5003
|
+
function getResolvedApiKeyFilePath(serviceName, instance, localPath, local, global) {
|
|
5004
|
+
if (!instance.apiKeyFile)
|
|
4957
5005
|
return;
|
|
4958
|
-
|
|
4959
|
-
|
|
5006
|
+
const localInstances = local.services?.[serviceName] ?? [];
|
|
5007
|
+
const localMatch = localInstances.find((i2) => i2.apiKeyFile && (instance.name ? i2.name === instance.name : true));
|
|
5008
|
+
if (localMatch) {
|
|
5009
|
+
return resolveConfigRelativePath(instance.apiKeyFile, localPath);
|
|
4960
5010
|
}
|
|
4961
|
-
|
|
4962
|
-
|
|
5011
|
+
const globalInstances = global.services?.[serviceName] ?? [];
|
|
5012
|
+
const globalMatch = globalInstances.find((i2) => i2.apiKeyFile && (instance.name ? i2.name === instance.name : true));
|
|
5013
|
+
if (globalMatch) {
|
|
5014
|
+
return resolveConfigRelativePath(instance.apiKeyFile, GLOBAL_CONFIG_PATH);
|
|
4963
5015
|
}
|
|
4964
|
-
return
|
|
5016
|
+
return instance.apiKeyFile;
|
|
4965
5017
|
}
|
|
4966
5018
|
function readApiKeyFile(filePath) {
|
|
4967
5019
|
if (!existsSync(filePath)) {
|
|
@@ -4973,75 +5025,132 @@ function readApiKeyFile(filePath) {
|
|
|
4973
5025
|
throw new Error(`Failed to read API key file: ${filePath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
4974
5026
|
}
|
|
4975
5027
|
}
|
|
4976
|
-
function
|
|
5028
|
+
function getServiceInstances(serviceName) {
|
|
5029
|
+
const config = loadConfig();
|
|
5030
|
+
return config.services[serviceName] ?? [];
|
|
5031
|
+
}
|
|
5032
|
+
function resolveInstance(instances, instanceName) {
|
|
5033
|
+
if (!instances.length)
|
|
5034
|
+
return;
|
|
5035
|
+
if (!instanceName)
|
|
5036
|
+
return instances[0];
|
|
5037
|
+
return instances.find((i2) => i2.name?.toLowerCase() === instanceName.toLowerCase());
|
|
5038
|
+
}
|
|
5039
|
+
function getServiceConfig(serviceName, instanceName) {
|
|
4977
5040
|
const global = readJsonFile(GLOBAL_CONFIG_PATH);
|
|
4978
5041
|
const localPath = findLocalConfigPath();
|
|
4979
5042
|
const local = localPath ? readJsonFile(localPath) : {};
|
|
4980
5043
|
const config = loadConfig();
|
|
4981
|
-
const
|
|
4982
|
-
|
|
5044
|
+
const instances = config.services[serviceName] ?? [];
|
|
5045
|
+
const instance = resolveInstance(instances, instanceName);
|
|
5046
|
+
if (!instance?.baseUrl)
|
|
4983
5047
|
return null;
|
|
4984
5048
|
if (serviceName === "qbittorrent") {
|
|
4985
|
-
if (!
|
|
5049
|
+
if (!instance.username || !instance.password)
|
|
4986
5050
|
return null;
|
|
4987
5051
|
return {
|
|
4988
|
-
baseUrl:
|
|
4989
|
-
username:
|
|
4990
|
-
password:
|
|
4991
|
-
...
|
|
5052
|
+
baseUrl: instance.baseUrl,
|
|
5053
|
+
username: instance.username,
|
|
5054
|
+
password: instance.password,
|
|
5055
|
+
...instance.timeout ? { timeout: instance.timeout } : {}
|
|
4992
5056
|
};
|
|
4993
5057
|
}
|
|
4994
|
-
let apiKey =
|
|
4995
|
-
const apiKeyFilePath = getResolvedApiKeyFilePath(serviceName,
|
|
5058
|
+
let apiKey = instance.apiKey;
|
|
5059
|
+
const apiKeyFilePath = getResolvedApiKeyFilePath(serviceName, instance, localPath, local, global);
|
|
4996
5060
|
if (!apiKey && apiKeyFilePath) {
|
|
4997
5061
|
apiKey = readApiKeyFile(apiKeyFilePath);
|
|
4998
5062
|
}
|
|
4999
5063
|
if (!apiKey)
|
|
5000
5064
|
return null;
|
|
5001
5065
|
return {
|
|
5002
|
-
baseUrl:
|
|
5066
|
+
baseUrl: instance.baseUrl,
|
|
5003
5067
|
apiKey,
|
|
5004
|
-
...
|
|
5068
|
+
...instance.timeout ? { timeout: instance.timeout } : {}
|
|
5005
5069
|
};
|
|
5006
5070
|
}
|
|
5071
|
+
function serializeForDisk(config) {
|
|
5072
|
+
const services = {};
|
|
5073
|
+
for (const [name, instances] of Object.entries(config.services)) {
|
|
5074
|
+
if (instances.length === 1 && !instances[0].name) {
|
|
5075
|
+
const { name: _name, ...rest } = instances[0];
|
|
5076
|
+
services[name] = rest;
|
|
5077
|
+
} else {
|
|
5078
|
+
services[name] = instances;
|
|
5079
|
+
}
|
|
5080
|
+
}
|
|
5081
|
+
return { services, defaults: config.defaults };
|
|
5082
|
+
}
|
|
5007
5083
|
function saveGlobalConfig(config) {
|
|
5008
5084
|
mkdirSync(GLOBAL_CONFIG_DIR, { recursive: true });
|
|
5009
|
-
writeFileSync(GLOBAL_CONFIG_PATH, `${JSON.stringify(config, null, 2)}
|
|
5085
|
+
writeFileSync(GLOBAL_CONFIG_PATH, `${JSON.stringify(serializeForDisk(config), null, 2)}
|
|
5010
5086
|
`);
|
|
5011
5087
|
}
|
|
5012
5088
|
function saveLocalConfig(config) {
|
|
5013
5089
|
const existingPath = findLocalConfigPath();
|
|
5014
5090
|
const targetPath = existingPath ?? join(process.cwd(), LOCAL_CONFIG_NAME);
|
|
5015
|
-
writeFileSync(targetPath, `${JSON.stringify(config, null, 2)}
|
|
5091
|
+
writeFileSync(targetPath, `${JSON.stringify(serializeForDisk(config), null, 2)}
|
|
5016
5092
|
`);
|
|
5017
5093
|
}
|
|
5094
|
+
function resolveArrayPath(instances, segment) {
|
|
5095
|
+
const byName = instances.find((i2) => i2.name === segment);
|
|
5096
|
+
if (byName)
|
|
5097
|
+
return byName;
|
|
5098
|
+
const idx = Number(segment);
|
|
5099
|
+
if (Number.isInteger(idx) && idx >= 0 && idx < instances.length)
|
|
5100
|
+
return instances[idx];
|
|
5101
|
+
return;
|
|
5102
|
+
}
|
|
5018
5103
|
function getConfigValue(key) {
|
|
5019
5104
|
const config = loadConfig();
|
|
5020
5105
|
const parts = key.split(".");
|
|
5021
5106
|
let current = config;
|
|
5022
|
-
for (
|
|
5107
|
+
for (let i2 = 0;i2 < parts.length; i2++) {
|
|
5023
5108
|
if (current == null || typeof current !== "object")
|
|
5024
5109
|
return;
|
|
5025
|
-
current
|
|
5110
|
+
if (Array.isArray(current)) {
|
|
5111
|
+
const resolved = resolveArrayPath(current, parts[i2]);
|
|
5112
|
+
if (resolved) {
|
|
5113
|
+
current = resolved;
|
|
5114
|
+
continue;
|
|
5115
|
+
}
|
|
5116
|
+
current = current[0]?.[parts[i2]];
|
|
5117
|
+
} else {
|
|
5118
|
+
current = current[parts[i2]];
|
|
5119
|
+
}
|
|
5026
5120
|
}
|
|
5027
5121
|
return current != null ? String(current) : undefined;
|
|
5028
5122
|
}
|
|
5029
5123
|
function setConfigValue(key, value, global = true) {
|
|
5030
5124
|
const configPath = global ? GLOBAL_CONFIG_PATH : findLocalConfigPath() ?? join(process.cwd(), LOCAL_CONFIG_NAME);
|
|
5031
|
-
const
|
|
5125
|
+
const raw = existsSync(configPath) ? JSON.parse(readFileSync(configPath, "utf-8")) : {};
|
|
5032
5126
|
const parts = key.split(".");
|
|
5033
|
-
let current =
|
|
5127
|
+
let current = raw;
|
|
5034
5128
|
for (let i2 = 0;i2 < parts.length - 1; i2++) {
|
|
5035
|
-
|
|
5036
|
-
|
|
5129
|
+
const part = parts[i2];
|
|
5130
|
+
if (Array.isArray(current[part])) {
|
|
5131
|
+
const nextPart = parts[i2 + 1];
|
|
5132
|
+
const resolved = resolveArrayPath(current[part], nextPart);
|
|
5133
|
+
if (resolved) {
|
|
5134
|
+
current = resolved;
|
|
5135
|
+
i2++;
|
|
5136
|
+
continue;
|
|
5137
|
+
}
|
|
5138
|
+
current = current[part][0];
|
|
5139
|
+
continue;
|
|
5140
|
+
}
|
|
5141
|
+
if (current[part] == null || typeof current[part] !== "object") {
|
|
5142
|
+
current[part] = {};
|
|
5037
5143
|
}
|
|
5038
|
-
current = current[
|
|
5144
|
+
current = current[part];
|
|
5039
5145
|
}
|
|
5040
5146
|
current[parts[parts.length - 1]] = parseConfigValue(key, value);
|
|
5041
5147
|
if (global) {
|
|
5042
|
-
|
|
5148
|
+
mkdirSync(GLOBAL_CONFIG_DIR, { recursive: true });
|
|
5149
|
+
writeFileSync(configPath, `${JSON.stringify(raw, null, 2)}
|
|
5150
|
+
`);
|
|
5043
5151
|
} else {
|
|
5044
|
-
|
|
5152
|
+
writeFileSync(configPath, `${JSON.stringify(raw, null, 2)}
|
|
5153
|
+
`);
|
|
5045
5154
|
}
|
|
5046
5155
|
}
|
|
5047
5156
|
function parseConfigValue(key, value) {
|
|
@@ -5339,6 +5448,11 @@ function buildServiceCommand(serviceName, description, clientFactory, resources)
|
|
|
5339
5448
|
description: "Cherry-pick fields (comma-separated, JSON mode)"
|
|
5340
5449
|
},
|
|
5341
5450
|
yes: { type: "boolean", alias: "y", description: "Skip confirmation prompts" },
|
|
5451
|
+
instance: {
|
|
5452
|
+
type: "string",
|
|
5453
|
+
alias: "i",
|
|
5454
|
+
description: "Instance name (for multi-instance services)"
|
|
5455
|
+
},
|
|
5342
5456
|
...(action.args ?? []).reduce((acc, arg) => {
|
|
5343
5457
|
acc[arg.name] = {
|
|
5344
5458
|
type: arg.type === "boolean" ? "boolean" : arg.type === "number" ? "string" : "string",
|
|
@@ -5349,10 +5463,17 @@ function buildServiceCommand(serviceName, description, clientFactory, resources)
|
|
|
5349
5463
|
}, {})
|
|
5350
5464
|
},
|
|
5351
5465
|
async run({ args }) {
|
|
5352
|
-
const
|
|
5466
|
+
const instanceName = args.instance;
|
|
5467
|
+
const config = getServiceConfig(serviceName, instanceName);
|
|
5353
5468
|
if (!config) {
|
|
5354
|
-
|
|
5355
|
-
|
|
5469
|
+
if (instanceName) {
|
|
5470
|
+
const available = getServiceInstances(serviceName).map((i2) => i2.name).filter(Boolean);
|
|
5471
|
+
const hint = available.length ? ` Available: ${available.join(", ")}` : "";
|
|
5472
|
+
consola.error(`${serviceName} instance '${instanceName}' is not configured.${hint}`);
|
|
5473
|
+
} else {
|
|
5474
|
+
const envHint = serviceName === "qbittorrent" ? `TSARR_QBITTORRENT_URL, TSARR_QBITTORRENT_USERNAME, and TSARR_QBITTORRENT_PASSWORD` : `TSARR_${serviceName.toUpperCase()}_URL and TSARR_${serviceName.toUpperCase()}_API_KEY`;
|
|
5475
|
+
consola.error(`${serviceName} is not configured. Run \`tsarr config init\` or set ${envHint} environment variables.`);
|
|
5476
|
+
}
|
|
5356
5477
|
process.exit(1);
|
|
5357
5478
|
}
|
|
5358
5479
|
try {
|
|
@@ -5380,7 +5501,7 @@ function buildServiceCommand(serviceName, description, clientFactory, resources)
|
|
|
5380
5501
|
const noHeader = process.argv.includes("--no-header") || !!args.noHeader;
|
|
5381
5502
|
const dryRun = !!(args["dry-run"] ?? args.dryRun ?? process.argv.includes("--dry-run"));
|
|
5382
5503
|
if (dryRun) {
|
|
5383
|
-
formatOutput(buildDryRunPreview(format, serviceName, resource.name, action.name, resolvedArgs), {
|
|
5504
|
+
formatOutput(buildDryRunPreview(format, serviceName, resource.name, action.name, resolvedArgs, instanceName), {
|
|
5384
5505
|
format,
|
|
5385
5506
|
noHeader
|
|
5386
5507
|
});
|
|
@@ -5511,19 +5632,21 @@ function coerceBooleanArg(value) {
|
|
|
5511
5632
|
}
|
|
5512
5633
|
return Boolean(value);
|
|
5513
5634
|
}
|
|
5514
|
-
function buildDryRunPreview(format, serviceName, resourceName, actionName, args) {
|
|
5515
|
-
const filteredArgs = Object.fromEntries(Object.entries(args).filter(([key, value]) => value !== undefined && key !== "_" && key !== "json" && key !== "table" && key !== "plain" && key !== "quiet" && key !== "select" && key !== "no-header" && key !== "noHeader" && key !== "dry-run" && key !== "dryRun"));
|
|
5635
|
+
function buildDryRunPreview(format, serviceName, resourceName, actionName, args, instanceName) {
|
|
5636
|
+
const filteredArgs = Object.fromEntries(Object.entries(args).filter(([key, value]) => value !== undefined && key !== "_" && key !== "json" && key !== "table" && key !== "plain" && key !== "quiet" && key !== "select" && key !== "no-header" && key !== "noHeader" && key !== "dry-run" && key !== "dryRun" && key !== "instance"));
|
|
5637
|
+
const serviceLabel = instanceName ? `${serviceName}[${instanceName}]` : serviceName;
|
|
5516
5638
|
if (format === "json") {
|
|
5517
5639
|
return {
|
|
5518
5640
|
dryRun: true,
|
|
5519
5641
|
service: serviceName,
|
|
5642
|
+
...instanceName ? { instance: instanceName } : {},
|
|
5520
5643
|
resource: resourceName,
|
|
5521
5644
|
action: actionName,
|
|
5522
5645
|
args: filteredArgs
|
|
5523
5646
|
};
|
|
5524
5647
|
}
|
|
5525
5648
|
return {
|
|
5526
|
-
message: `Dry run: would execute ${
|
|
5649
|
+
message: `Dry run: would execute ${serviceLabel} ${resourceName} ${actionName}${formatDryRunArgs(filteredArgs)}`
|
|
5527
5650
|
};
|
|
5528
5651
|
}
|
|
5529
5652
|
function formatDryRunArgs(args) {
|
|
@@ -21201,9 +21324,10 @@ var init_doctor = __esm(() => {
|
|
|
21201
21324
|
consola.info(`Checking connections...
|
|
21202
21325
|
`);
|
|
21203
21326
|
}
|
|
21327
|
+
let hasMultiInstance = false;
|
|
21204
21328
|
for (const service of SERVICES) {
|
|
21205
|
-
const
|
|
21206
|
-
if (
|
|
21329
|
+
const instances = getServiceInstances(service);
|
|
21330
|
+
if (instances.length === 0) {
|
|
21207
21331
|
results.push({
|
|
21208
21332
|
service,
|
|
21209
21333
|
configured: false,
|
|
@@ -21211,45 +21335,62 @@ var init_doctor = __esm(() => {
|
|
|
21211
21335
|
});
|
|
21212
21336
|
continue;
|
|
21213
21337
|
}
|
|
21214
|
-
|
|
21215
|
-
|
|
21216
|
-
|
|
21217
|
-
const
|
|
21218
|
-
if (
|
|
21219
|
-
|
|
21220
|
-
|
|
21221
|
-
|
|
21222
|
-
|
|
21223
|
-
|
|
21338
|
+
if (instances.length > 1)
|
|
21339
|
+
hasMultiInstance = true;
|
|
21340
|
+
for (const inst of instances) {
|
|
21341
|
+
const svcConfig = getServiceConfig(service, inst.name);
|
|
21342
|
+
if (!svcConfig) {
|
|
21343
|
+
results.push({
|
|
21344
|
+
service,
|
|
21345
|
+
...inst.name ? { instance: inst.name } : {},
|
|
21346
|
+
configured: false,
|
|
21347
|
+
status: "not configured"
|
|
21348
|
+
});
|
|
21349
|
+
continue;
|
|
21224
21350
|
}
|
|
21225
|
-
|
|
21226
|
-
|
|
21227
|
-
|
|
21351
|
+
hasAny = true;
|
|
21352
|
+
try {
|
|
21353
|
+
const client9 = clientFactories[service](svcConfig);
|
|
21354
|
+
const status = await client9.getSystemStatus();
|
|
21355
|
+
if (status?.error !== undefined) {
|
|
21356
|
+
const err = status.error;
|
|
21357
|
+
const code = err?.cause?.code ?? err?.code;
|
|
21358
|
+
const cause = code ? { code } : undefined;
|
|
21359
|
+
const message = err?.cause?.message ?? err?.message ?? err?.code ?? "Unknown API error";
|
|
21360
|
+
throw Object.assign(new Error(message), cause ? { cause } : {});
|
|
21361
|
+
}
|
|
21362
|
+
const version = extractVersion(service, status) ?? "?";
|
|
21363
|
+
if (version === "?") {
|
|
21364
|
+
throw new Error("Unexpected response payload");
|
|
21365
|
+
}
|
|
21366
|
+
results.push({
|
|
21367
|
+
service,
|
|
21368
|
+
...inst.name ? { instance: inst.name } : {},
|
|
21369
|
+
configured: true,
|
|
21370
|
+
status: "ok",
|
|
21371
|
+
version: String(version),
|
|
21372
|
+
baseUrl: svcConfig.baseUrl
|
|
21373
|
+
});
|
|
21374
|
+
} catch (error) {
|
|
21375
|
+
results.push({
|
|
21376
|
+
service,
|
|
21377
|
+
...inst.name ? { instance: inst.name } : {},
|
|
21378
|
+
configured: true,
|
|
21379
|
+
status: "fail",
|
|
21380
|
+
baseUrl: svcConfig.baseUrl,
|
|
21381
|
+
error: classifyError(error)
|
|
21382
|
+
});
|
|
21228
21383
|
}
|
|
21229
|
-
results.push({
|
|
21230
|
-
service,
|
|
21231
|
-
configured: true,
|
|
21232
|
-
status: "ok",
|
|
21233
|
-
version: String(version),
|
|
21234
|
-
baseUrl: svcConfig.baseUrl
|
|
21235
|
-
});
|
|
21236
|
-
} catch (error) {
|
|
21237
|
-
results.push({
|
|
21238
|
-
service,
|
|
21239
|
-
configured: true,
|
|
21240
|
-
status: "fail",
|
|
21241
|
-
baseUrl: svcConfig.baseUrl,
|
|
21242
|
-
error: classifyError(error)
|
|
21243
|
-
});
|
|
21244
21384
|
}
|
|
21245
21385
|
}
|
|
21246
21386
|
const hadFailure = !hasAny || results.some((r3) => r3.status === "fail");
|
|
21247
21387
|
if (!hasAny && format === "table") {
|
|
21248
21388
|
consola.warn("\nNo services configured. Run `tsarr config init` to set up.");
|
|
21249
21389
|
}
|
|
21390
|
+
const columns = hasMultiInstance ? ["service", "instance", "status", "configured", "version", "baseUrl", "error"] : ["service", "status", "configured", "version", "baseUrl", "error"];
|
|
21250
21391
|
formatOutput(results, {
|
|
21251
21392
|
format,
|
|
21252
|
-
columns
|
|
21393
|
+
columns,
|
|
21253
21394
|
idField: "service",
|
|
21254
21395
|
select: args.select
|
|
21255
21396
|
});
|
|
@@ -21265,6 +21406,61 @@ var exports_config = {};
|
|
|
21265
21406
|
__export(exports_config, {
|
|
21266
21407
|
config: () => config
|
|
21267
21408
|
});
|
|
21409
|
+
async function configureInstance(service, instanceName) {
|
|
21410
|
+
const baseUrl = await promptIfMissing(undefined, `${service}${instanceName ? ` (${instanceName})` : ""} base URL (e.g. http://localhost:${DEFAULT_PORTS[service]})`);
|
|
21411
|
+
if (service === "qbittorrent") {
|
|
21412
|
+
const username = await promptIfMissing(undefined, `${service} username`);
|
|
21413
|
+
const password = await promptIfMissing(undefined, `${service} password`);
|
|
21414
|
+
const cfg2 = { baseUrl, username, password };
|
|
21415
|
+
if (instanceName)
|
|
21416
|
+
cfg2.name = instanceName;
|
|
21417
|
+
return cfg2;
|
|
21418
|
+
}
|
|
21419
|
+
const apiKey = await promptIfMissing(undefined, `${service} API key`);
|
|
21420
|
+
const cfg = { baseUrl, apiKey };
|
|
21421
|
+
if (instanceName)
|
|
21422
|
+
cfg.name = instanceName;
|
|
21423
|
+
return cfg;
|
|
21424
|
+
}
|
|
21425
|
+
async function testConnection(service, serviceConfig) {
|
|
21426
|
+
try {
|
|
21427
|
+
if (service === "qbittorrent") {
|
|
21428
|
+
const { QBittorrentClient: QBittorrentClient2 } = await Promise.resolve().then(() => (init_qbittorrent2(), exports_qbittorrent));
|
|
21429
|
+
const client9 = new QBittorrentClient2({
|
|
21430
|
+
baseUrl: serviceConfig.baseUrl,
|
|
21431
|
+
username: serviceConfig.username,
|
|
21432
|
+
password: serviceConfig.password
|
|
21433
|
+
});
|
|
21434
|
+
const status = await client9.getSystemStatus();
|
|
21435
|
+
consola.success(`Connected to ${service}${serviceConfig.name ? ` (${serviceConfig.name})` : ""} v${status.version}`);
|
|
21436
|
+
} else {
|
|
21437
|
+
const { RadarrClient: RadarrClient2 } = await Promise.resolve().then(() => (init_radarr2(), exports_radarr));
|
|
21438
|
+
const { SonarrClient: SonarrClient2 } = await Promise.resolve().then(() => (init_sonarr2(), exports_sonarr));
|
|
21439
|
+
const { LidarrClient: LidarrClient2 } = await Promise.resolve().then(() => (init_lidarr2(), exports_lidarr));
|
|
21440
|
+
const { ReadarrClient: ReadarrClient2 } = await Promise.resolve().then(() => (init_readarr2(), exports_readarr));
|
|
21441
|
+
const { ProwlarrClient: ProwlarrClient2 } = await Promise.resolve().then(() => (init_prowlarr2(), exports_prowlarr));
|
|
21442
|
+
const { BazarrClient: BazarrClient2 } = await Promise.resolve().then(() => (init_bazarr2(), exports_bazarr));
|
|
21443
|
+
const { SeerrClient: SeerrClient2 } = await Promise.resolve().then(() => (init_seerr2(), exports_seerr));
|
|
21444
|
+
const factories = {
|
|
21445
|
+
radarr: (c3) => new RadarrClient2(c3),
|
|
21446
|
+
sonarr: (c3) => new SonarrClient2(c3),
|
|
21447
|
+
lidarr: (c3) => new LidarrClient2(c3),
|
|
21448
|
+
readarr: (c3) => new ReadarrClient2(c3),
|
|
21449
|
+
prowlarr: (c3) => new ProwlarrClient2(c3),
|
|
21450
|
+
bazarr: (c3) => new BazarrClient2(c3),
|
|
21451
|
+
seerr: (c3) => new SeerrClient2(c3)
|
|
21452
|
+
};
|
|
21453
|
+
const client9 = factories[service]?.(serviceConfig);
|
|
21454
|
+
if (client9) {
|
|
21455
|
+
const status = await client9.getSystemStatus();
|
|
21456
|
+
const version = status?.data?.version ?? status?.version ?? "?";
|
|
21457
|
+
consola.success(`Connected to ${service}${serviceConfig.name ? ` (${serviceConfig.name})` : ""} v${version}`);
|
|
21458
|
+
}
|
|
21459
|
+
}
|
|
21460
|
+
} catch {
|
|
21461
|
+
consola.warn(`Could not connect to ${service}${serviceConfig.name ? ` (${serviceConfig.name})` : ""} — config saved anyway.`);
|
|
21462
|
+
}
|
|
21463
|
+
}
|
|
21268
21464
|
var DEFAULT_PORTS, configInit, configSet, configGet, configShow, config;
|
|
21269
21465
|
var init_config2 = __esm(() => {
|
|
21270
21466
|
init_dist();
|
|
@@ -21304,49 +21500,25 @@ var init_config2 = __esm(() => {
|
|
|
21304
21500
|
const config = { services: {} };
|
|
21305
21501
|
for (const service of selected) {
|
|
21306
21502
|
console.log();
|
|
21307
|
-
const
|
|
21308
|
-
|
|
21309
|
-
|
|
21310
|
-
|
|
21311
|
-
|
|
21312
|
-
|
|
21313
|
-
|
|
21314
|
-
|
|
21315
|
-
|
|
21316
|
-
|
|
21317
|
-
|
|
21318
|
-
consola.warn(`Could not connect to ${service} — config saved anyway.`);
|
|
21319
|
-
}
|
|
21320
|
-
} else {
|
|
21321
|
-
const apiKey = await promptIfMissing(undefined, `${service} API key`);
|
|
21322
|
-
config.services[service] = { baseUrl, apiKey };
|
|
21323
|
-
try {
|
|
21324
|
-
const { RadarrClient: RadarrClient2 } = await Promise.resolve().then(() => (init_radarr2(), exports_radarr));
|
|
21325
|
-
const { SonarrClient: SonarrClient2 } = await Promise.resolve().then(() => (init_sonarr2(), exports_sonarr));
|
|
21326
|
-
const { LidarrClient: LidarrClient2 } = await Promise.resolve().then(() => (init_lidarr2(), exports_lidarr));
|
|
21327
|
-
const { ReadarrClient: ReadarrClient2 } = await Promise.resolve().then(() => (init_readarr2(), exports_readarr));
|
|
21328
|
-
const { ProwlarrClient: ProwlarrClient2 } = await Promise.resolve().then(() => (init_prowlarr2(), exports_prowlarr));
|
|
21329
|
-
const { BazarrClient: BazarrClient2 } = await Promise.resolve().then(() => (init_bazarr2(), exports_bazarr));
|
|
21330
|
-
const { SeerrClient: SeerrClient2 } = await Promise.resolve().then(() => (init_seerr2(), exports_seerr));
|
|
21331
|
-
const factories = {
|
|
21332
|
-
radarr: (c3) => new RadarrClient2(c3),
|
|
21333
|
-
sonarr: (c3) => new SonarrClient2(c3),
|
|
21334
|
-
lidarr: (c3) => new LidarrClient2(c3),
|
|
21335
|
-
readarr: (c3) => new ReadarrClient2(c3),
|
|
21336
|
-
prowlarr: (c3) => new ProwlarrClient2(c3),
|
|
21337
|
-
bazarr: (c3) => new BazarrClient2(c3),
|
|
21338
|
-
seerr: (c3) => new SeerrClient2(c3)
|
|
21339
|
-
};
|
|
21340
|
-
const client9 = factories[service]?.(config.services[service]);
|
|
21341
|
-
if (client9) {
|
|
21342
|
-
const status = await client9.getSystemStatus();
|
|
21343
|
-
const version = status?.data?.version ?? status?.version ?? "?";
|
|
21344
|
-
consola.success(`Connected to ${service} v${version}`);
|
|
21345
|
-
}
|
|
21346
|
-
} catch {
|
|
21347
|
-
consola.warn(`Could not connect to ${service} — config saved anyway.`);
|
|
21503
|
+
const instances = [];
|
|
21504
|
+
const first = await configureInstance(service);
|
|
21505
|
+
await testConnection(service, first);
|
|
21506
|
+
instances.push(first);
|
|
21507
|
+
while (true) {
|
|
21508
|
+
const addMore = await promptConfirm(`Add another ${service} instance?`);
|
|
21509
|
+
if (!addMore)
|
|
21510
|
+
break;
|
|
21511
|
+
if (instances.length === 1 && !instances[0].name) {
|
|
21512
|
+
const firstName = await promptIfMissing(undefined, `Name for the existing ${service} instance (e.g. "main", "1080p")`);
|
|
21513
|
+
instances[0].name = firstName;
|
|
21348
21514
|
}
|
|
21515
|
+
const newName = await promptIfMissing(undefined, `Name for the new ${service} instance (e.g. "4K")`);
|
|
21516
|
+
console.log();
|
|
21517
|
+
const newInstance = await configureInstance(service, newName);
|
|
21518
|
+
await testConnection(service, newInstance);
|
|
21519
|
+
instances.push(newInstance);
|
|
21349
21520
|
}
|
|
21521
|
+
config.services[service] = instances;
|
|
21350
21522
|
}
|
|
21351
21523
|
const location = await promptSelect("Save config to:", [
|
|
21352
21524
|
{ label: `Global (${GLOBAL_CONFIG_PATH})`, value: "global" },
|
|
@@ -21409,11 +21581,15 @@ var init_config2 = __esm(() => {
|
|
|
21409
21581
|
const config = loadConfig();
|
|
21410
21582
|
const redacted = JSON.parse(JSON.stringify(config));
|
|
21411
21583
|
if (redacted.services) {
|
|
21412
|
-
for (const
|
|
21413
|
-
if (
|
|
21414
|
-
svc
|
|
21415
|
-
|
|
21416
|
-
|
|
21584
|
+
for (const instances of Object.values(redacted.services)) {
|
|
21585
|
+
if (Array.isArray(instances)) {
|
|
21586
|
+
for (const svc of instances) {
|
|
21587
|
+
if (svc?.apiKey)
|
|
21588
|
+
svc.apiKey = "*****";
|
|
21589
|
+
if (svc?.password)
|
|
21590
|
+
svc.password = "*****";
|
|
21591
|
+
}
|
|
21592
|
+
}
|
|
21417
21593
|
}
|
|
21418
21594
|
}
|
|
21419
21595
|
console.log(JSON.stringify(redacted, null, 2));
|
|
@@ -21528,7 +21704,8 @@ ${actionAssoc}
|
|
|
21528
21704
|
'--json[Output as JSON]' \\
|
|
21529
21705
|
'--table[Output as table]' \\
|
|
21530
21706
|
'--quiet[Output IDs only]' \\
|
|
21531
|
-
'--yes[Skip confirmation prompts]'
|
|
21707
|
+
'--yes[Skip confirmation prompts]' \\
|
|
21708
|
+
'--instance[Instance name for multi-instance services]:instance:'
|
|
21532
21709
|
;;
|
|
21533
21710
|
esac
|
|
21534
21711
|
}
|
|
@@ -21565,7 +21742,9 @@ ${actionCompletions}
|
|
|
21565
21742
|
complete -c tsarr -l json -d "Output as JSON"
|
|
21566
21743
|
complete -c tsarr -l table -d "Output as table"
|
|
21567
21744
|
complete -c tsarr -l quiet -s q -d "Output IDs only"
|
|
21568
|
-
complete -c tsarr -l yes -s y -d "Skip confirmation prompts"
|
|
21745
|
+
complete -c tsarr -l yes -s y -d "Skip confirmation prompts"
|
|
21746
|
+
complete -c tsarr -l instance -s i -d "Instance name (multi-instance services)"
|
|
21747
|
+
`;
|
|
21569
21748
|
}
|
|
21570
21749
|
var SERVICE_COMMANDS, completions;
|
|
21571
21750
|
var init_completions = __esm(() => {
|
|
@@ -21664,7 +21843,7 @@ init_dist();
|
|
|
21664
21843
|
// package.json
|
|
21665
21844
|
var package_default = {
|
|
21666
21845
|
name: "tsarr",
|
|
21667
|
-
version: "2.
|
|
21846
|
+
version: "2.8.0",
|
|
21668
21847
|
author: "Robbe Verhelst",
|
|
21669
21848
|
repository: {
|
|
21670
21849
|
type: "git",
|
|
@@ -21676,7 +21855,7 @@ var package_default = {
|
|
|
21676
21855
|
main: "dist/index.js",
|
|
21677
21856
|
module: "dist/index.js",
|
|
21678
21857
|
devDependencies: {
|
|
21679
|
-
"@biomejs/biome": "2.4.
|
|
21858
|
+
"@biomejs/biome": "2.4.12",
|
|
21680
21859
|
"@hey-api/openapi-ts": "^0.96.0",
|
|
21681
21860
|
"@semantic-release/changelog": "^6.0.3",
|
|
21682
21861
|
"@semantic-release/git": "^10.0.1",
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tsarr",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.8.0",
|
|
4
4
|
"author": "Robbe Verhelst",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"main": "dist/index.js",
|
|
13
13
|
"module": "dist/index.js",
|
|
14
14
|
"devDependencies": {
|
|
15
|
-
"@biomejs/biome": "2.4.
|
|
15
|
+
"@biomejs/biome": "2.4.12",
|
|
16
16
|
"@hey-api/openapi-ts": "^0.96.0",
|
|
17
17
|
"@semantic-release/changelog": "^6.0.3",
|
|
18
18
|
"@semantic-release/git": "^10.0.1",
|
package/dist/tsarr-2.7.6.tgz
DELETED
|
Binary file
|