@onpeek/nuvio 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.
- package/LICENSE +22 -0
- package/README.md +83 -0
- package/assets/nuvio-wordmark-dark.png +0 -0
- package/assets/nuvio-wordmark-light.png +0 -0
- package/dist/cli/confirmation.js +45 -0
- package/dist/cli/registry.js +14 -0
- package/dist/commands/account.js +66 -0
- package/dist/commands/addons.js +84 -0
- package/dist/commands/collections.js +147 -0
- package/dist/commands/helpers.js +186 -0
- package/dist/commands/index.js +26 -0
- package/dist/commands/library.js +105 -0
- package/dist/commands/plugins.js +58 -0
- package/dist/commands/profiles.js +126 -0
- package/dist/commands/providers.js +57 -0
- package/dist/commands/sessions.js +46 -0
- package/dist/commands/settings.js +43 -0
- package/dist/commands/trackers.js +84 -0
- package/dist/commands/undo.js +165 -0
- package/dist/config.js +60 -0
- package/dist/index.js +195 -0
- package/dist/mask.js +33 -0
- package/dist/nuvio/auth.js +123 -0
- package/dist/nuvio/call-context.js +20 -0
- package/dist/nuvio/client.js +149 -0
- package/dist/nuvio/errors.js +43 -0
- package/dist/nuvio/keys.js +23 -0
- package/dist/nuvio/ops/account.js +91 -0
- package/dist/nuvio/ops/addons.js +126 -0
- package/dist/nuvio/ops/collections.js +158 -0
- package/dist/nuvio/ops/library.js +139 -0
- package/dist/nuvio/ops/plan.js +728 -0
- package/dist/nuvio/ops/plugins.js +127 -0
- package/dist/nuvio/ops/profiles.js +383 -0
- package/dist/nuvio/ops/providers.js +103 -0
- package/dist/nuvio/ops/readers.js +57 -0
- package/dist/nuvio/ops/sessions.js +36 -0
- package/dist/nuvio/ops/settings.js +143 -0
- package/dist/nuvio/ops/trackers.js +80 -0
- package/dist/nuvio/ops/transitions.js +100 -0
- package/dist/nuvio/paths.js +74 -0
- package/dist/nuvio/safe-fetch.js +131 -0
- package/dist/nuvio/schemas.js +143 -0
- package/dist/nuvio/snapshots.js +664 -0
- package/dist/nuvio/types.js +1 -0
- package/dist/version.js +12 -0
- package/package.json +62 -0
- package/skills/nuvio/SKILL.md +19 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { definePlanMutation } from './helpers.js';
|
|
2
|
+
import { registerProfileCommands } from './profiles.js';
|
|
3
|
+
import { registerAddonCommands } from './addons.js';
|
|
4
|
+
import { registerPluginCommands } from './plugins.js';
|
|
5
|
+
import { registerSettingsCommands } from './settings.js';
|
|
6
|
+
import { registerCollectionCommands } from './collections.js';
|
|
7
|
+
import { registerLibraryCommands } from './library.js';
|
|
8
|
+
import { registerProviderCommands } from './providers.js';
|
|
9
|
+
import { registerTrackerCommands } from './trackers.js';
|
|
10
|
+
import { registerSessionCommands } from './sessions.js';
|
|
11
|
+
import { registerAccountCommands } from './account.js';
|
|
12
|
+
import { registerUndoCommands } from './undo.js';
|
|
13
|
+
export function registerAllCommands(registry, client, cfg) {
|
|
14
|
+
registerProfileCommands(registry, client, cfg);
|
|
15
|
+
registerAddonCommands(registry, client, cfg);
|
|
16
|
+
registerPluginCommands(registry, client, cfg);
|
|
17
|
+
registerSettingsCommands(registry, client, cfg);
|
|
18
|
+
registerCollectionCommands(registry, client, cfg);
|
|
19
|
+
registerLibraryCommands(registry, client, cfg);
|
|
20
|
+
registerProviderCommands(registry, client, cfg);
|
|
21
|
+
registerTrackerCommands(registry, client, cfg);
|
|
22
|
+
registerSessionCommands(registry, client, cfg);
|
|
23
|
+
registerAccountCommands(registry, client, cfg);
|
|
24
|
+
registerUndoCommands(registry, client, cfg);
|
|
25
|
+
definePlanMutation(registry, client, cfg);
|
|
26
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { defineMutation, defineRead } from './helpers.js';
|
|
3
|
+
import { historyAddShape, historyDeleteShape, libraryAddShape, libraryRemoveShape, progressDeleteShape, progressSetShape, profile, } from '../nuvio/schemas.js';
|
|
4
|
+
import * as library from '../nuvio/ops/library.js';
|
|
5
|
+
export function registerLibraryCommands(registry, client, cfg) {
|
|
6
|
+
const originId = cfg.originClientId;
|
|
7
|
+
const resource = (kind) => (args) => ({ kind, profile_id: args.profile_id });
|
|
8
|
+
defineRead(registry, client, cfg, {
|
|
9
|
+
name: 'nuvio_get_library',
|
|
10
|
+
title: 'Get library',
|
|
11
|
+
description: "List items in a profile's library (bookmarks/favourites).",
|
|
12
|
+
risk: 'read',
|
|
13
|
+
schema: {
|
|
14
|
+
profile_id: profile,
|
|
15
|
+
limit: z.number().int().min(1).max(1000).default(100),
|
|
16
|
+
offset: z.number().int().min(0).default(0),
|
|
17
|
+
},
|
|
18
|
+
handler: (args) => library.getLibrary(client, args.profile_id, args.limit, args.offset),
|
|
19
|
+
});
|
|
20
|
+
defineMutation(registry, client, cfg, {
|
|
21
|
+
name: 'nuvio_add_to_library',
|
|
22
|
+
title: 'Add to library',
|
|
23
|
+
description: 'Add or update library items in bulk (upsert by content_id + content_type).',
|
|
24
|
+
risk: 'write',
|
|
25
|
+
resource: resource('library'),
|
|
26
|
+
scope: (args) => args.items.map((i) => ({ content_id: i.content_id, content_type: i.content_type })),
|
|
27
|
+
schema: libraryAddShape,
|
|
28
|
+
handler: (args, ctx) => library.addToLibrary(client, args.profile_id, args.items, originId, ctx.apply),
|
|
29
|
+
});
|
|
30
|
+
defineMutation(registry, client, cfg, {
|
|
31
|
+
name: 'nuvio_remove_from_library',
|
|
32
|
+
title: 'Remove from library',
|
|
33
|
+
description: "Remove items from a profile's library by content_id + content_type.",
|
|
34
|
+
risk: 'destructive',
|
|
35
|
+
resource: resource('library'),
|
|
36
|
+
scope: (args) => args.keys,
|
|
37
|
+
schema: libraryRemoveShape,
|
|
38
|
+
handler: (args, ctx) => library.removeFromLibrary(client, args.profile_id, args.keys, originId, ctx.apply),
|
|
39
|
+
});
|
|
40
|
+
defineRead(registry, client, cfg, {
|
|
41
|
+
name: 'nuvio_get_watch_progress',
|
|
42
|
+
title: 'Get watch progress',
|
|
43
|
+
description: 'List "continue watching" progress entries for a profile.',
|
|
44
|
+
risk: 'read',
|
|
45
|
+
schema: { profile_id: profile, limit: z.number().int().min(1).max(100000).default(100) },
|
|
46
|
+
handler: (args) => library.getWatchProgress(client, args.profile_id, args.limit),
|
|
47
|
+
});
|
|
48
|
+
defineMutation(registry, client, cfg, {
|
|
49
|
+
name: 'nuvio_set_watch_progress',
|
|
50
|
+
title: 'Set watch progress',
|
|
51
|
+
description: 'Create or update continue-watching entries (position/duration) for movies or episodes, in bulk.',
|
|
52
|
+
risk: 'write',
|
|
53
|
+
resource: resource('watch_progress'),
|
|
54
|
+
scope: (args) => args.entries.map((e) => library.progressKeyOf(e)),
|
|
55
|
+
schema: progressSetShape,
|
|
56
|
+
handler: (args, ctx) => library.setWatchProgress(client, args.profile_id, args.entries, originId, ctx.apply),
|
|
57
|
+
});
|
|
58
|
+
defineMutation(registry, client, cfg, {
|
|
59
|
+
name: 'nuvio_delete_watch_progress',
|
|
60
|
+
title: 'Delete watch progress',
|
|
61
|
+
description: 'Delete continue-watching entries using structured keys (content_id + optional season/episode).',
|
|
62
|
+
risk: 'destructive',
|
|
63
|
+
resource: resource('watch_progress'),
|
|
64
|
+
scope: (args) => args.keys.map((k) => library.progressKeyOf(k)),
|
|
65
|
+
schema: progressDeleteShape,
|
|
66
|
+
handler: (args, ctx) => library.deleteWatchProgress(client, args.profile_id, args.keys, originId, ctx.apply),
|
|
67
|
+
});
|
|
68
|
+
defineRead(registry, client, cfg, {
|
|
69
|
+
name: 'nuvio_get_watch_history',
|
|
70
|
+
title: 'Get watch history',
|
|
71
|
+
description: 'List watched items for a profile.',
|
|
72
|
+
risk: 'read',
|
|
73
|
+
schema: {
|
|
74
|
+
profile_id: profile,
|
|
75
|
+
page: z.number().int().min(1).default(1).describe('Page number (1-based)'),
|
|
76
|
+
page_size: z.number().int().min(1).max(1000).default(100),
|
|
77
|
+
},
|
|
78
|
+
handler: (args) => library.getWatchHistory(client, args.profile_id, args.page, args.page_size),
|
|
79
|
+
});
|
|
80
|
+
defineMutation(registry, client, cfg, {
|
|
81
|
+
name: 'nuvio_add_to_watch_history',
|
|
82
|
+
title: 'Add to watch history',
|
|
83
|
+
description: "Idempotently add watched items to a profile's history (upsert by content_id + season + episode). " +
|
|
84
|
+
'A repeated identical call does not create a duplicate.',
|
|
85
|
+
risk: 'write',
|
|
86
|
+
resource: resource('watch_history'),
|
|
87
|
+
scope: (args) => args.items.map((i) => ({
|
|
88
|
+
content_id: i.content_id,
|
|
89
|
+
season: i.season ?? null,
|
|
90
|
+
episode: i.episode ?? null,
|
|
91
|
+
})),
|
|
92
|
+
schema: historyAddShape,
|
|
93
|
+
handler: (args, ctx) => library.addToWatchHistory(client, args.profile_id, args.items, originId, ctx.apply),
|
|
94
|
+
});
|
|
95
|
+
defineMutation(registry, client, cfg, {
|
|
96
|
+
name: 'nuvio_delete_watch_history',
|
|
97
|
+
title: 'Delete watch history',
|
|
98
|
+
description: 'Delete watch-history entries by content id (and season/episode).',
|
|
99
|
+
risk: 'destructive',
|
|
100
|
+
resource: resource('watch_history'),
|
|
101
|
+
scope: (args) => args.keys,
|
|
102
|
+
schema: historyDeleteShape,
|
|
103
|
+
handler: (args, ctx) => library.deleteWatchHistory(client, args.profile_id, args.keys, originId, ctx.apply),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { defineMutation, defineRead } from './helpers.js';
|
|
2
|
+
import { assertTarget, pluginAddShape, pluginRemoveShape, pluginReorderShape, pluginUpdateShape, profile, } from '../nuvio/schemas.js';
|
|
3
|
+
import * as plugins from '../nuvio/ops/plugins.js';
|
|
4
|
+
export function registerPluginCommands(registry, client, cfg) {
|
|
5
|
+
const originId = cfg.originClientId;
|
|
6
|
+
const resource = (args) => ({ kind: 'plugins', profile_id: args.profile_id });
|
|
7
|
+
defineRead(registry, client, cfg, {
|
|
8
|
+
name: 'nuvio_list_plugins',
|
|
9
|
+
title: 'List plugins',
|
|
10
|
+
description: 'List plugins installed on a profile.',
|
|
11
|
+
risk: 'read',
|
|
12
|
+
schema: { profile_id: profile },
|
|
13
|
+
handler: (args) => plugins.listPlugins(client, args.profile_id),
|
|
14
|
+
});
|
|
15
|
+
defineMutation(registry, client, cfg, {
|
|
16
|
+
name: 'nuvio_add_plugin',
|
|
17
|
+
title: 'Install a plugin',
|
|
18
|
+
description: 'Install a plugin on a profile by URL, preserving the existing plugin list.',
|
|
19
|
+
risk: 'write',
|
|
20
|
+
resource,
|
|
21
|
+
schema: pluginAddShape,
|
|
22
|
+
handler: (args, ctx) => plugins.addPlugin(client, args.profile_id, args, originId, ctx.apply),
|
|
23
|
+
});
|
|
24
|
+
defineMutation(registry, client, cfg, {
|
|
25
|
+
name: 'nuvio_update_plugin',
|
|
26
|
+
title: 'Update a plugin',
|
|
27
|
+
description: 'Change a plugin name, enabled flag, repo type or sort order. Identify it by url or table id.',
|
|
28
|
+
risk: 'write',
|
|
29
|
+
resource,
|
|
30
|
+
schema: pluginUpdateShape,
|
|
31
|
+
handler: (args, ctx) => {
|
|
32
|
+
assertTarget('nuvio_update_plugin', args);
|
|
33
|
+
const { profile_id, url, id, ...changes } = args;
|
|
34
|
+
return plugins.updatePlugin(client, profile_id, { url, id }, changes, originId, ctx.apply);
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
defineMutation(registry, client, cfg, {
|
|
38
|
+
name: 'nuvio_reorder_plugins',
|
|
39
|
+
title: 'Reorder plugins',
|
|
40
|
+
description: 'Set plugin order. Provide every installed plugin URL exactly once, in the desired order.',
|
|
41
|
+
risk: 'write',
|
|
42
|
+
resource,
|
|
43
|
+
schema: pluginReorderShape,
|
|
44
|
+
handler: (args, ctx) => plugins.reorderPlugins(client, args.profile_id, args.ordered_urls, originId, ctx.apply),
|
|
45
|
+
});
|
|
46
|
+
defineMutation(registry, client, cfg, {
|
|
47
|
+
name: 'nuvio_remove_plugin',
|
|
48
|
+
title: 'Remove a plugin',
|
|
49
|
+
description: 'Uninstall a plugin from a profile.',
|
|
50
|
+
risk: 'destructive',
|
|
51
|
+
resource,
|
|
52
|
+
schema: pluginRemoveShape,
|
|
53
|
+
handler: (args, ctx) => {
|
|
54
|
+
assertTarget('nuvio_remove_plugin', args);
|
|
55
|
+
return plugins.removePlugin(client, args.profile_id, { url: args.url, id: args.id }, originId, ctx.apply);
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { defineMutation, defineRead } from './helpers.js';
|
|
3
|
+
import * as profiles from '../nuvio/ops/profiles.js';
|
|
4
|
+
const index = z.number().int().min(1).max(6);
|
|
5
|
+
export function registerProfileCommands(registry, client, cfg) {
|
|
6
|
+
const originId = cfg.originClientId;
|
|
7
|
+
defineRead(registry, client, cfg, {
|
|
8
|
+
name: 'nuvio_list_profiles',
|
|
9
|
+
title: 'List Nuvio profiles',
|
|
10
|
+
description: 'List all profiles (slot 1..6, name, avatar, flags, PIN lock state).',
|
|
11
|
+
risk: 'read',
|
|
12
|
+
schema: {},
|
|
13
|
+
handler: () => profiles.listProfiles(client),
|
|
14
|
+
});
|
|
15
|
+
defineMutation(registry, client, cfg, {
|
|
16
|
+
name: 'nuvio_create_profile',
|
|
17
|
+
title: 'Create a Nuvio profile',
|
|
18
|
+
description: 'Add a new profile, preserving every existing one. Max 6 profiles; slot 1 is primary.',
|
|
19
|
+
risk: 'write',
|
|
20
|
+
resource: { kind: 'profiles' },
|
|
21
|
+
schema: {
|
|
22
|
+
name: z.string().min(1),
|
|
23
|
+
profile_id: index.optional().describe('Slot 1..6; auto-picked if omitted'),
|
|
24
|
+
avatar_color_hex: z
|
|
25
|
+
.string()
|
|
26
|
+
.regex(/^#[0-9a-fA-F]{6}$/)
|
|
27
|
+
.optional(),
|
|
28
|
+
avatar_id: z.string().optional().describe('Avatar catalog id (see nuvio_list_avatars)'),
|
|
29
|
+
avatar_url: z.url().optional(),
|
|
30
|
+
uses_primary_addons: z.boolean().optional().describe('Share addons with profile 1'),
|
|
31
|
+
},
|
|
32
|
+
handler: (args, ctx) => profiles.createProfile(client, {
|
|
33
|
+
name: args.name,
|
|
34
|
+
profile_index: args.profile_id,
|
|
35
|
+
avatar_color_hex: args.avatar_color_hex,
|
|
36
|
+
avatar_id: args.avatar_id,
|
|
37
|
+
avatar_url: args.avatar_url,
|
|
38
|
+
uses_primary_addons: args.uses_primary_addons,
|
|
39
|
+
}, originId, ctx.apply),
|
|
40
|
+
});
|
|
41
|
+
defineMutation(registry, client, cfg, {
|
|
42
|
+
name: 'nuvio_update_profile',
|
|
43
|
+
title: 'Update a Nuvio profile',
|
|
44
|
+
description: 'Sparsely update one profile (name, avatar color, avatar id/url, uses_primary_addons).',
|
|
45
|
+
risk: 'write',
|
|
46
|
+
resource: { kind: 'profiles' },
|
|
47
|
+
schema: {
|
|
48
|
+
profile_id: index,
|
|
49
|
+
name: z.string().min(1).optional(),
|
|
50
|
+
avatar_color_hex: z
|
|
51
|
+
.string()
|
|
52
|
+
.regex(/^#[0-9a-fA-F]{6}$/)
|
|
53
|
+
.optional(),
|
|
54
|
+
avatar_id: z.string().nullable().optional(),
|
|
55
|
+
avatar_url: z.url().nullable().optional(),
|
|
56
|
+
uses_primary_addons: z.boolean().optional(),
|
|
57
|
+
},
|
|
58
|
+
handler: (args, ctx) => profiles.updateProfile(client, args.profile_id, {
|
|
59
|
+
name: args.name,
|
|
60
|
+
avatar_color_hex: args.avatar_color_hex,
|
|
61
|
+
...('avatar_id' in args ? { avatar_id: args.avatar_id } : {}),
|
|
62
|
+
...('avatar_url' in args ? { avatar_url: args.avatar_url } : {}),
|
|
63
|
+
uses_primary_addons: args.uses_primary_addons,
|
|
64
|
+
}, ctx.apply),
|
|
65
|
+
});
|
|
66
|
+
defineMutation(registry, client, cfg, {
|
|
67
|
+
name: 'nuvio_delete_profile',
|
|
68
|
+
title: 'Delete a Nuvio profile',
|
|
69
|
+
description: 'Delete a profile and ALL of its data (addons, settings, collections, library, history). Profile 1 cannot be deleted and this cannot be undone.',
|
|
70
|
+
risk: 'destructive',
|
|
71
|
+
resource: { kind: 'profiles' },
|
|
72
|
+
reversible: false,
|
|
73
|
+
note: 'profile data is deleted permanently and cannot be restored from a snapshot',
|
|
74
|
+
schema: { profile_id: index.min(2) },
|
|
75
|
+
handler: (args, ctx) => profiles.deleteProfile(client, args.profile_id, originId, ctx.apply),
|
|
76
|
+
});
|
|
77
|
+
defineMutation(registry, client, cfg, {
|
|
78
|
+
name: 'nuvio_copy_setup',
|
|
79
|
+
title: 'Copy setup between profiles',
|
|
80
|
+
description: 'Copy a profile setup to another profile, optionally across platforms (platforms: [{from,to}], defaults to ' +
|
|
81
|
+
'identity per platform). Settings are deep-merged (settings_mode=merge) or replaced (replace); provider ' +
|
|
82
|
+
'credentials can be left alone, merged, or replaced. Transactional: on failure it rolls back and reports ' +
|
|
83
|
+
'whether the rollback completed.',
|
|
84
|
+
risk: 'write',
|
|
85
|
+
resource: (args) => ({ kind: 'profile_setup', profile_id: args.target_profile_id }),
|
|
86
|
+
schema: {
|
|
87
|
+
source_profile_id: index,
|
|
88
|
+
target_profile_id: index,
|
|
89
|
+
platforms: z
|
|
90
|
+
.array(z.object({ from: z.enum(profiles.SETUP_PLATFORMS), to: z.enum(profiles.SETUP_PLATFORMS) }))
|
|
91
|
+
.optional()
|
|
92
|
+
.describe('Platform mappings (e.g. tv -> mobile). Defaults to tv->tv, mobile->mobile, desktop->desktop.'),
|
|
93
|
+
settings_mode: z.enum(['merge', 'replace']).optional().default('merge'),
|
|
94
|
+
provider_credentials: z.enum(['none', 'merge', 'replace']).optional().default('none'),
|
|
95
|
+
},
|
|
96
|
+
handler: (args, ctx) => profiles.copySetup(client, {
|
|
97
|
+
source_profile_id: args.source_profile_id,
|
|
98
|
+
target_profile_id: args.target_profile_id,
|
|
99
|
+
platforms: args.platforms,
|
|
100
|
+
settings_mode: args.settings_mode,
|
|
101
|
+
provider_credentials: args.provider_credentials,
|
|
102
|
+
}, originId, ctx.apply),
|
|
103
|
+
});
|
|
104
|
+
defineMutation(registry, client, cfg, {
|
|
105
|
+
name: 'nuvio_set_profile_pin',
|
|
106
|
+
title: 'Set a profile PIN',
|
|
107
|
+
description: 'Set or change the PIN lock on a profile. Not reversible (the PIN hash is never returned).',
|
|
108
|
+
risk: 'destructive',
|
|
109
|
+
resource: { kind: 'profiles' },
|
|
110
|
+
reversible: false,
|
|
111
|
+
note: 'PIN changes cannot be reversed automatically',
|
|
112
|
+
schema: { profile_id: index, pin: z.string().min(1), current_pin: z.string().optional() },
|
|
113
|
+
handler: (args, ctx) => profiles.setProfilePin(client, args.profile_id, args.pin, args.current_pin, ctx.apply),
|
|
114
|
+
});
|
|
115
|
+
defineMutation(registry, client, cfg, {
|
|
116
|
+
name: 'nuvio_clear_profile_pin',
|
|
117
|
+
title: 'Clear a profile PIN',
|
|
118
|
+
description: 'Remove the PIN lock from a profile. Not reversible.',
|
|
119
|
+
risk: 'destructive',
|
|
120
|
+
resource: { kind: 'profiles' },
|
|
121
|
+
reversible: false,
|
|
122
|
+
note: 'PIN changes cannot be reversed automatically',
|
|
123
|
+
schema: { profile_id: index, current_pin: z.string().optional() },
|
|
124
|
+
handler: (args, ctx) => profiles.clearProfilePin(client, args.profile_id, args.current_pin, ctx.apply),
|
|
125
|
+
});
|
|
126
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { defineMutation, defineRead } from './helpers.js';
|
|
3
|
+
import { PROVIDER_CREDENTIAL_FIELD, listProviderCredentials, setProviderCredential, deleteProviderCredential, testProviderCredential, } from '../nuvio/ops/providers.js';
|
|
4
|
+
import { providerSetShape } from '../nuvio/schemas.js';
|
|
5
|
+
export function registerProviderCommands(registry, client, cfg) {
|
|
6
|
+
const originId = cfg.originClientId;
|
|
7
|
+
const profile = z.number().int().min(1).max(6).default(1);
|
|
8
|
+
const resource = (args) => ({ kind: 'provider_credentials', profile_id: args.profile_id });
|
|
9
|
+
defineRead(registry, client, cfg, {
|
|
10
|
+
name: 'nuvio_list_provider_credentials',
|
|
11
|
+
title: 'List provider credentials',
|
|
12
|
+
description: 'List configured providers for a profile (debrid services, TMDB, MDBList, AniSkip, IntroDB). Secret values are masked.',
|
|
13
|
+
risk: 'read',
|
|
14
|
+
schema: { profile_id: profile },
|
|
15
|
+
handler: async (args) => {
|
|
16
|
+
const rows = await listProviderCredentials(client, args.profile_id);
|
|
17
|
+
return {
|
|
18
|
+
supported_providers: Object.keys(PROVIDER_CREDENTIAL_FIELD),
|
|
19
|
+
configured: rows.map((row) => ({
|
|
20
|
+
provider: row.provider,
|
|
21
|
+
configured: true,
|
|
22
|
+
updated_at: row.updated_at,
|
|
23
|
+
})),
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
defineMutation(registry, client, cfg, {
|
|
28
|
+
name: 'nuvio_set_provider_credential',
|
|
29
|
+
title: 'Set a provider credential',
|
|
30
|
+
description: 'Store an API key for a provider: debrid:torbox, debrid:premiumize, debrid:realdebrid, tmdb, mdblist, introdb (api key) or animeskip (client id).',
|
|
31
|
+
risk: 'write',
|
|
32
|
+
resource,
|
|
33
|
+
schema: providerSetShape,
|
|
34
|
+
handler: (args, ctx) => setProviderCredential(client, args.profile_id, args.provider, args.api_key, originId, ctx.apply),
|
|
35
|
+
});
|
|
36
|
+
defineRead(registry, client, cfg, {
|
|
37
|
+
name: 'nuvio_test_provider_credential',
|
|
38
|
+
title: 'Test a provider credential',
|
|
39
|
+
description: 'Verify a provider credential without storing it. The secret is never returned or logged; the result ' +
|
|
40
|
+
'reports format validity and, where the provider exposes a cheap endpoint, a live check.',
|
|
41
|
+
risk: 'read',
|
|
42
|
+
schema: {
|
|
43
|
+
provider: z.string().describe('One of the supported provider ids'),
|
|
44
|
+
api_key: z.string().min(1).describe('API key / client id value to verify'),
|
|
45
|
+
},
|
|
46
|
+
handler: (args) => testProviderCredential(args.provider, args.api_key),
|
|
47
|
+
});
|
|
48
|
+
defineMutation(registry, client, cfg, {
|
|
49
|
+
name: 'nuvio_delete_provider_credential',
|
|
50
|
+
title: 'Delete a provider credential',
|
|
51
|
+
description: 'Remove a stored provider credential from a profile.',
|
|
52
|
+
risk: 'destructive',
|
|
53
|
+
resource,
|
|
54
|
+
schema: { profile_id: profile, provider: z.string() },
|
|
55
|
+
handler: (args, ctx) => deleteProviderCredential(client, args.profile_id, args.provider, originId, ctx.apply),
|
|
56
|
+
});
|
|
57
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { defineMutation, defineRead } from './helpers.js';
|
|
3
|
+
import * as sessions from '../nuvio/ops/sessions.js';
|
|
4
|
+
export function registerSessionCommands(registry, client, cfg) {
|
|
5
|
+
defineRead(registry, client, cfg, {
|
|
6
|
+
name: 'nuvio_list_sessions',
|
|
7
|
+
title: 'List signed-in devices',
|
|
8
|
+
description: 'List active login sessions/devices (device name, platform, client, last active, which is current).',
|
|
9
|
+
risk: 'read',
|
|
10
|
+
schema: {},
|
|
11
|
+
handler: () => sessions.listSessions(client),
|
|
12
|
+
});
|
|
13
|
+
defineMutation(registry, client, cfg, {
|
|
14
|
+
name: 'nuvio_revoke_session',
|
|
15
|
+
title: 'Log out a device',
|
|
16
|
+
description: 'Revoke one login session (log that device out). Cannot be undone — the device must sign in again. Never deletes the account.',
|
|
17
|
+
risk: 'destructive',
|
|
18
|
+
resource: { kind: 'sessions' },
|
|
19
|
+
reversible: false,
|
|
20
|
+
note: 'revoked sessions cannot be recreated; the device needs to sign in again',
|
|
21
|
+
schema: { session_id: z.uuid() },
|
|
22
|
+
handler: (args, ctx) => sessions.revokeSession(client, args.session_id, ctx.apply),
|
|
23
|
+
});
|
|
24
|
+
defineMutation(registry, client, cfg, {
|
|
25
|
+
name: 'nuvio_register_device',
|
|
26
|
+
title: 'Register this device',
|
|
27
|
+
description: "Register a device/installation against the account. client_name must be one the Nuvio backend accepts (for example 'Nuvio Web'); other values are rejected by the backend.",
|
|
28
|
+
risk: 'write',
|
|
29
|
+
resource: { kind: 'sessions' },
|
|
30
|
+
reversible: false,
|
|
31
|
+
schema: {
|
|
32
|
+
installation_id: z.string(),
|
|
33
|
+
client_name: z.string(),
|
|
34
|
+
client_version: z.string().optional(),
|
|
35
|
+
device_name: z.string().optional(),
|
|
36
|
+
platform: z.string().optional(),
|
|
37
|
+
},
|
|
38
|
+
handler: (args, ctx) => sessions.registerDevice(client, {
|
|
39
|
+
installation_id: args.installation_id,
|
|
40
|
+
client_name: args.client_name,
|
|
41
|
+
client_version: args.client_version,
|
|
42
|
+
device_name: args.device_name,
|
|
43
|
+
platform: args.platform,
|
|
44
|
+
}, ctx.apply),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { defineMutation, defineRead } from './helpers.js';
|
|
2
|
+
import { getSettingsShape, updateSettingsShape } from '../nuvio/schemas.js';
|
|
3
|
+
import * as settings from '../nuvio/ops/settings.js';
|
|
4
|
+
export function registerSettingsCommands(registry, client, cfg) {
|
|
5
|
+
const originId = cfg.originClientId;
|
|
6
|
+
const settingsResource = (args) => ({ kind: 'settings', profile_id: args.profile_id, platform: args.platform });
|
|
7
|
+
const homeResource = (args) => ({ kind: 'home_catalog_settings', profile_id: args.profile_id, platform: args.platform });
|
|
8
|
+
defineRead(registry, client, cfg, {
|
|
9
|
+
name: 'nuvio_get_settings',
|
|
10
|
+
title: 'Get profile settings',
|
|
11
|
+
description: 'Read the JSON settings blob for a profile on a platform.',
|
|
12
|
+
risk: 'read',
|
|
13
|
+
schema: getSettingsShape,
|
|
14
|
+
handler: (args) => settings.getSettings(client, args.profile_id, args.platform),
|
|
15
|
+
});
|
|
16
|
+
defineMutation(registry, client, cfg, {
|
|
17
|
+
name: 'nuvio_update_settings',
|
|
18
|
+
title: 'Update profile settings',
|
|
19
|
+
description: 'Update a profile settings blob. `patch` is deep-merged (nested keys are preserved, arrays and scalars ' +
|
|
20
|
+
'replace), `set` assigns nested dot paths and `unset` deletes them — applied in the order patch, set, unset.',
|
|
21
|
+
risk: 'write',
|
|
22
|
+
resource: settingsResource,
|
|
23
|
+
schema: updateSettingsShape,
|
|
24
|
+
handler: (args, ctx) => settings.updateSettings(client, args.profile_id, args.platform, args, originId, ctx.apply),
|
|
25
|
+
});
|
|
26
|
+
defineMutation(registry, client, cfg, {
|
|
27
|
+
name: 'nuvio_update_home_catalog_settings',
|
|
28
|
+
title: 'Update home catalog settings',
|
|
29
|
+
description: 'Update the home screen layout/catalog blob with the same patch/set/unset semantics as nuvio_update_settings.',
|
|
30
|
+
risk: 'write',
|
|
31
|
+
resource: homeResource,
|
|
32
|
+
schema: updateSettingsShape,
|
|
33
|
+
handler: (args, ctx) => settings.updateHomeCatalogSettings(client, args.profile_id, args.platform, args, originId, ctx.apply),
|
|
34
|
+
});
|
|
35
|
+
defineRead(registry, client, cfg, {
|
|
36
|
+
name: 'nuvio_get_home_catalog_settings',
|
|
37
|
+
title: 'Get home catalog settings',
|
|
38
|
+
description: 'Read the home screen layout/catalog configuration for a profile on a platform.',
|
|
39
|
+
risk: 'read',
|
|
40
|
+
schema: getSettingsShape,
|
|
41
|
+
handler: (args) => settings.getHomeCatalogSettings(client, args.profile_id, args.platform),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { defineMutation, defineRead } from './helpers.js';
|
|
3
|
+
import { TRACKERS, listTrackerTokens, listTrackerSettings, setTrackerSettings, setTrackerToken, unlinkTracker, } from '../nuvio/ops/trackers.js';
|
|
4
|
+
export function registerTrackerCommands(registry, client, cfg) {
|
|
5
|
+
const profile = z.number().int().min(1).max(6).default(1);
|
|
6
|
+
const tracker = z.enum(TRACKERS).describe('Tracker: mal | anilist | kitsu');
|
|
7
|
+
defineRead(registry, client, cfg, {
|
|
8
|
+
name: 'nuvio_list_trackers',
|
|
9
|
+
title: 'List trackers',
|
|
10
|
+
description: 'List linked trackers (MAL / AniList / Kitsu) and their per-profile settings. Access tokens are masked.',
|
|
11
|
+
risk: 'read',
|
|
12
|
+
schema: { profile_id: profile },
|
|
13
|
+
handler: async (args) => {
|
|
14
|
+
const [tokens, settings] = await Promise.all([
|
|
15
|
+
listTrackerTokens(client, args.profile_id),
|
|
16
|
+
listTrackerSettings(client, args.profile_id),
|
|
17
|
+
]);
|
|
18
|
+
const byTracker = new Map(settings.map((s) => [s.tracker, s]));
|
|
19
|
+
return TRACKERS.map((name) => {
|
|
20
|
+
const token = tokens.find((t) => t.tracker === name);
|
|
21
|
+
const setting = byTracker.get(name);
|
|
22
|
+
return {
|
|
23
|
+
tracker: name,
|
|
24
|
+
linked: Boolean(token),
|
|
25
|
+
username: token?.tracker_username ?? null,
|
|
26
|
+
expires_at: token?.expires_at ?? null,
|
|
27
|
+
enabled_statuses: setting?.enabled_statuses ?? [],
|
|
28
|
+
send_progress: setting?.send_progress ?? null,
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
defineMutation(registry, client, cfg, {
|
|
34
|
+
name: 'nuvio_set_tracker_settings',
|
|
35
|
+
title: 'Update tracker settings',
|
|
36
|
+
description: 'Set which statuses sync, row order and progress syncing for a linked tracker.',
|
|
37
|
+
risk: 'write',
|
|
38
|
+
resource: (args) => ({ kind: 'tracker_settings', profile_id: args.profile_id }),
|
|
39
|
+
schema: {
|
|
40
|
+
profile_id: profile,
|
|
41
|
+
tracker,
|
|
42
|
+
enabled_statuses: z.array(z.string()).optional(),
|
|
43
|
+
row_order: z.array(z.string()).optional(),
|
|
44
|
+
send_progress: z.boolean().optional(),
|
|
45
|
+
},
|
|
46
|
+
handler: (args, ctx) => setTrackerSettings(client, args.profile_id, args.tracker, {
|
|
47
|
+
enabled_statuses: args.enabled_statuses,
|
|
48
|
+
row_order: args.row_order,
|
|
49
|
+
send_progress: args.send_progress,
|
|
50
|
+
}, ctx.apply),
|
|
51
|
+
});
|
|
52
|
+
defineMutation(registry, client, cfg, {
|
|
53
|
+
name: 'nuvio_link_tracker',
|
|
54
|
+
title: 'Link a tracker',
|
|
55
|
+
description: 'Store tracker OAuth tokens for a profile. Use this only when tokens were obtained out-of-band; prefer the app sign-in flow.',
|
|
56
|
+
risk: 'write',
|
|
57
|
+
resource: (args) => ({ kind: 'tracker_tokens', profile_id: args.profile_id }),
|
|
58
|
+
schema: {
|
|
59
|
+
profile_id: profile,
|
|
60
|
+
tracker,
|
|
61
|
+
access_token: z.string().min(1),
|
|
62
|
+
refresh_token: z.string().optional(),
|
|
63
|
+
expires_in_seconds: z.number().int().positive().optional(),
|
|
64
|
+
tracker_user_id: z.string().optional(),
|
|
65
|
+
username: z.string().optional(),
|
|
66
|
+
},
|
|
67
|
+
handler: (args, ctx) => setTrackerToken(client, args.profile_id, args.tracker, {
|
|
68
|
+
access_token: args.access_token,
|
|
69
|
+
refresh_token: args.refresh_token,
|
|
70
|
+
expires_in_seconds: args.expires_in_seconds,
|
|
71
|
+
tracker_user_id: args.tracker_user_id,
|
|
72
|
+
username: args.username,
|
|
73
|
+
}, ctx.apply),
|
|
74
|
+
});
|
|
75
|
+
defineMutation(registry, client, cfg, {
|
|
76
|
+
name: 'nuvio_unlink_tracker',
|
|
77
|
+
title: 'Unlink a tracker',
|
|
78
|
+
description: 'Remove stored tokens for a tracker on a profile.',
|
|
79
|
+
risk: 'destructive',
|
|
80
|
+
resource: (args) => ({ kind: 'tracker_tokens', profile_id: args.profile_id }),
|
|
81
|
+
schema: { profile_id: profile, tracker },
|
|
82
|
+
handler: (args, ctx) => unlinkTracker(client, args.profile_id, args.tracker, ctx.apply),
|
|
83
|
+
});
|
|
84
|
+
}
|