leetcode-capture-helper 0.0.0-stage → 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/cli.js +92 -0
- package/config.js +25 -0
- package/framing.js +57 -0
- package/host.js +346 -0
- package/install.js +290 -0
- package/package.json +31 -4
- package/runtime/bundle.json +42 -0
- package/runtime/companion/capture-log.js +83 -0
- package/runtime/companion/codex-backend.js +337 -0
- package/runtime/companion/companion.js +2193 -0
- package/runtime/companion/mouse-input.js +154 -0
- package/runtime/companion/package.json +4 -0
- package/runtime/companion/panel-bridge.js +273 -0
- package/runtime/companion/settings-rpc.js +228 -0
- package/runtime/companion/settings.js +464 -0
- package/runtime/companion/terminal-format.js +330 -0
- package/runtime/companion/vault-summary.js +169 -0
- package/runtime/companion/virtual-terminal.js +138 -0
- package/runtime/extension/shared/anthropic-backend.js +193 -0
- package/runtime/extension/shared/catalog.js +415 -0
- package/runtime/extension/shared/code-ledger.js +333 -0
- package/runtime/extension/shared/context-budget.js +259 -0
- package/runtime/extension/shared/context-summary.js +271 -0
- package/runtime/extension/shared/conversation.js +81 -0
- package/runtime/extension/shared/event-hub.js +126 -0
- package/runtime/extension/shared/extension-settings.js +445 -0
- package/runtime/extension/shared/helper-launcher.js +248 -0
- package/runtime/extension/shared/local-backend.js +706 -0
- package/runtime/extension/shared/local-server.js +149 -0
- package/runtime/extension/shared/package.json +4 -0
- package/runtime/extension/shared/practice.js +228 -0
- package/runtime/extension/shared/problem-index.js +4128 -0
- package/runtime/extension/shared/profiles.js +106 -0
- package/runtime/extension/shared/providers.js +58 -0
- package/runtime/extension/shared/reply-guard.js +864 -0
- package/runtime/extension/shared/roadmap.js +1344 -0
- package/runtime/extension/shared/sanitize.js +68 -0
- package/runtime/extension/shared/solved.js +277 -0
- package/runtime/extension/shared/tutor-engine.js +369 -0
- package/runtime/extension/shared/tutor-prompt.js +108 -0
- package/runtime/extension/shared/tutor-service.js +520 -0
- package/runtime/relay-server/package.json +4 -0
- package/runtime/relay-server/panel-broker.js +797 -0
- package/runtime/relay-server/server.js +241 -0
- package/runtime/vault-tool/log-session.js +230 -0
- package/runtime/vault-tool/package.json +4 -0
- package/runtime/vault-tool/vault-notes.js +206 -0
- package/README.md +0 -3
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// extension/shared/profiles.js - saved model profiles, the entries of the
|
|
2
|
+
// Tutor panel's model switcher. Shared by the extension's settings
|
|
3
|
+
// (extension-settings.js) and the companion's (companion/settings.js), so both
|
|
4
|
+
// stores keep them the same way.
|
|
5
|
+
//
|
|
6
|
+
// A profile is { id, provider, model, label }: which provider, which model of
|
|
7
|
+
// it (null means the provider's own default) and an optional name. Everything
|
|
8
|
+
// else a provider needs (its server URL, its API key, where it runs) stays in
|
|
9
|
+
// that provider's saved fields, shared by all of its profiles. A profile never
|
|
10
|
+
// carries a secret, so the whole list may be shown to the panel.
|
|
11
|
+
//
|
|
12
|
+
// Settings that predate profiles hold one provider and its model; they become
|
|
13
|
+
// one profile with the id DEFAULT_PROFILE_ID (see migrateProfiles).
|
|
14
|
+
//
|
|
15
|
+
// Pure JavaScript with no Node API (extension/shared.test.js checks that).
|
|
16
|
+
|
|
17
|
+
export const PROFILE_ID_RE = /^[a-z0-9][a-z0-9-]{0,39}$/;
|
|
18
|
+
export const MAX_PROFILES = 30;
|
|
19
|
+
export const MAX_PROFILE_LABEL = 60;
|
|
20
|
+
export const DEFAULT_PROFILE_ID = 'default';
|
|
21
|
+
|
|
22
|
+
// eslint-disable-next-line no-control-regex
|
|
23
|
+
const CONTROL_RE = /[\u0000-\u001f\u007f-\u009f]/;
|
|
24
|
+
|
|
25
|
+
function cleanText(value, max) {
|
|
26
|
+
if (value === null || value === undefined) return null;
|
|
27
|
+
if (typeof value !== 'string') return undefined;
|
|
28
|
+
const trimmed = value.trim();
|
|
29
|
+
if (!trimmed) return null;
|
|
30
|
+
if (trimmed.length > max || CONTROL_RE.test(trimmed)) return undefined;
|
|
31
|
+
return trimmed;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// One profile made safe, or null when it cannot be kept. `providers` is the
|
|
35
|
+
// catalog (providers.js's PROVIDERS).
|
|
36
|
+
export function cleanProfile(raw, providers) {
|
|
37
|
+
if (!raw || typeof raw !== 'object') return null;
|
|
38
|
+
if (typeof raw.id !== 'string' || !PROFILE_ID_RE.test(raw.id)) return null;
|
|
39
|
+
if (typeof raw.provider !== 'string' || !Object.prototype.hasOwnProperty.call(providers, raw.provider)) return null;
|
|
40
|
+
const model = cleanText(raw.model, 200);
|
|
41
|
+
const label = cleanText(raw.label, MAX_PROFILE_LABEL);
|
|
42
|
+
if (model === undefined || label === undefined) return null;
|
|
43
|
+
return { id: raw.id, provider: raw.provider, model, label };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// A list of profiles made safe: malformed entries, repeated ids and anything
|
|
47
|
+
// past MAX_PROFILES are left out. With `strict`, a bad entry throws a
|
|
48
|
+
// ProfileError instead (an options-page save is refused rather than quietly
|
|
49
|
+
// changed).
|
|
50
|
+
export class ProfileError extends Error {
|
|
51
|
+
constructor(message) {
|
|
52
|
+
super(message);
|
|
53
|
+
this.name = 'ProfileError';
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function cleanProfiles(raw, providers, { strict = false } = {}) {
|
|
58
|
+
if (raw === null || raw === undefined) return [];
|
|
59
|
+
if (!Array.isArray(raw)) {
|
|
60
|
+
if (strict) throw new ProfileError('profiles must be a list');
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
if (strict && raw.length > MAX_PROFILES) throw new ProfileError(`at most ${MAX_PROFILES} models can be saved`);
|
|
64
|
+
const out = [];
|
|
65
|
+
const ids = new Set();
|
|
66
|
+
for (const entry of raw) {
|
|
67
|
+
const profile = cleanProfile(entry, providers);
|
|
68
|
+
if (!profile || ids.has(profile.id)) {
|
|
69
|
+
if (strict) throw new ProfileError(profile ? `two saved models share the id "${profile.id}"` : 'a saved model is not valid');
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
ids.add(profile.id);
|
|
73
|
+
out.push(profile);
|
|
74
|
+
if (out.length >= MAX_PROFILES) break;
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function findProfile(profiles, id) {
|
|
80
|
+
if (typeof id !== 'string' || !Array.isArray(profiles)) return null;
|
|
81
|
+
return profiles.find((p) => p.id === id) || null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// The id kept as active: `id` when it names a profile, else null.
|
|
85
|
+
export function cleanActiveProfile(profiles, id) {
|
|
86
|
+
return findProfile(profiles, id) ? id : null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Settings from before profiles: one profile for the provider (and model)
|
|
90
|
+
// they chose, made the active one. `model` is that provider's saved model,
|
|
91
|
+
// or null. Returns { profiles, activeProfile }.
|
|
92
|
+
export function migrateProfiles({ profiles, activeProfile, provider, model }) {
|
|
93
|
+
if (Array.isArray(profiles) && profiles.length) {
|
|
94
|
+
return { profiles, activeProfile: cleanActiveProfile(profiles, activeProfile) };
|
|
95
|
+
}
|
|
96
|
+
if (!provider) return { profiles: [], activeProfile: null };
|
|
97
|
+
const profile = { id: DEFAULT_PROFILE_ID, provider, model: model || null, label: null };
|
|
98
|
+
return { profiles: [profile], activeProfile: profile.id };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// What a profile is called in the switcher and the options page: its own
|
|
102
|
+
// label, else its model, else its provider's label.
|
|
103
|
+
export function profileName(profile, providers) {
|
|
104
|
+
if (!profile) return '';
|
|
105
|
+
return profile.label || profile.model || (providers[profile.provider] && providers[profile.provider].label) || profile.provider;
|
|
106
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// extension/shared/providers.js - the tutor's provider catalog, shared by the
|
|
2
|
+
// companion's settings (companion/settings.js) and the extension's options
|
|
3
|
+
// page and background worker, so both list the same providers the same way.
|
|
4
|
+
//
|
|
5
|
+
// Pure data with no Node API (extension/shared.test.js checks that).
|
|
6
|
+
|
|
7
|
+
// `fields` are what the options page shows for a provider; `requires` must be
|
|
8
|
+
// present before a backend can be built for it. `runtimes` says where the
|
|
9
|
+
// tutor can run for it: 'extension' (inside the browser extension's own
|
|
10
|
+
// background worker, nothing to install) and/or 'helper' (the companion
|
|
11
|
+
// process on the user's machine, which the subscription providers need
|
|
12
|
+
// because they use a local sign-in). The first one listed is the default.
|
|
13
|
+
export const PROVIDERS = {
|
|
14
|
+
local: {
|
|
15
|
+
label: 'Local model',
|
|
16
|
+
hint: 'Ollama, LM Studio, or any OpenAI-compatible server running on your machine.',
|
|
17
|
+
fields: ['baseUrl', 'model', 'contextTokens', 'apiKey'],
|
|
18
|
+
requires: ['model'],
|
|
19
|
+
runtimes: ['extension', 'helper'],
|
|
20
|
+
},
|
|
21
|
+
claude: {
|
|
22
|
+
label: 'Claude subscription',
|
|
23
|
+
hint: 'Uses your Claude Code sign-in (Claude Pro or Max). Run "claude" once to sign in.',
|
|
24
|
+
fields: ['model'],
|
|
25
|
+
requires: [],
|
|
26
|
+
runtimes: ['helper'],
|
|
27
|
+
},
|
|
28
|
+
anthropic: {
|
|
29
|
+
label: 'Anthropic API key',
|
|
30
|
+
hint: 'Pay-as-you-go with a key from console.anthropic.com.',
|
|
31
|
+
fields: ['apiKey', 'model'],
|
|
32
|
+
requires: ['apiKey'],
|
|
33
|
+
runtimes: ['extension', 'helper'],
|
|
34
|
+
},
|
|
35
|
+
openai: {
|
|
36
|
+
label: 'OpenAI API key',
|
|
37
|
+
hint: 'Pay-as-you-go with a key from platform.openai.com.',
|
|
38
|
+
fields: ['apiKey', 'model', 'baseUrl'],
|
|
39
|
+
requires: ['apiKey', 'model'],
|
|
40
|
+
runtimes: ['extension', 'helper'],
|
|
41
|
+
},
|
|
42
|
+
chatgpt: {
|
|
43
|
+
label: 'ChatGPT subscription',
|
|
44
|
+
hint: 'Uses your Codex CLI sign-in (ChatGPT Plus, Pro, Business). Run "codex login" once.',
|
|
45
|
+
fields: ['model'],
|
|
46
|
+
requires: [],
|
|
47
|
+
runtimes: ['helper'],
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const OLLAMA_BASE_URL = 'http://localhost:11434/v1';
|
|
52
|
+
export const OPENAI_BASE_URL = 'https://api.openai.com/v1';
|
|
53
|
+
|
|
54
|
+
// The runtime a provider uses unless the user picked another one it allows.
|
|
55
|
+
export function defaultRuntime(provider) {
|
|
56
|
+
const spec = PROVIDERS[provider];
|
|
57
|
+
return spec ? spec.runtimes[0] : 'helper';
|
|
58
|
+
}
|