ossclip 0.1.33 → 0.1.34

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.
@@ -4,7 +4,7 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>ossclip editor</title>
7
- <script type="module" crossorigin src="/assets/index-DA7vDWn_.js"></script>
7
+ <script type="module" crossorigin src="/assets/index-Dn813PGp.js"></script>
8
8
  <link rel="stylesheet" crossorigin href="/assets/index-Bx2VQLP8.css">
9
9
  </head>
10
10
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ossclip",
3
- "version": "0.1.33",
3
+ "version": "0.1.34",
4
4
  "description": "Local-first CLI video producer: cuts silence and fillers, word-timed captions, face-aware framing, and LLM-planned code-rendered graphics — transcription and rendering never leave your machine",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -36,9 +36,9 @@
36
36
  "commander": "^12.1.0",
37
37
  "tsx": "^4.19.0",
38
38
  "zod": "^3.25.76",
39
- "@ossclip/core": "0.1.33",
40
- "@ossclip/renderer": "0.1.33",
41
- "@ossclip/scenes": "0.1.33"
39
+ "@ossclip/core": "0.1.34",
40
+ "@ossclip/renderer": "0.1.34",
41
+ "@ossclip/scenes": "0.1.34"
42
42
  },
43
43
  "homepage": "https://github.com/AhsanAyaz/ossclip#readme",
44
44
  "bugs": {
@@ -0,0 +1,74 @@
1
+ import { z } from "zod/v4";
2
+ import { defaultProviderName, providerOfLog, type ProviderName } from "@ossclip/core";
3
+ import { lastFlagValue } from "./thumbnail-panel";
4
+
5
+ /**
6
+ * Which LLM provider the publish panel's caption regenerate should call
7
+ * (2026-08-29), pure so the usage-log × pin × env matrix is a table test
8
+ * with no server and no filesystem — the thumbnail-panel split. The I/O half
9
+ * lives in edit.ts's /api/publish/regenerate handler, which reads usage.json
10
+ * and command.json and hands them here.
11
+ *
12
+ * Resolution order: the provider usage.json says actually planned this
13
+ * project (`providerOfLog` — truthful even after a §143 fallback, which is
14
+ * why it beats the pin), then the `--llm` pin in command.json's recorded
15
+ * args, then `defaultProviderName`'s detection. Every candidate is PARSED
16
+ * against the provider enum, never coerced: a hand-edited usage.json saying
17
+ * "geminni" falls through to the next rung instead of reaching
18
+ * createProvider as garbage (CLAUDE.md's --source-fit rule).
19
+ */
20
+
21
+ /** program.ts's `--llm` choices, restated as a schema — the same five names
22
+ * core's ProviderName union spells. */
23
+ const ProviderNameSchema = z.enum(["antigravity", "claude", "claude-cli", "gemini", "mock"]);
24
+
25
+ export type CaptionRegenProviderState =
26
+ | { status: "ready"; provider: ProviderName }
27
+ | { status: "unavailable"; reason: string };
28
+
29
+ export interface CaptionRegenProviderInputs {
30
+ /** usage.json parsed as-is, or null when absent/corrupt — read leniently
31
+ * by the caller (the /api/usage posture). */
32
+ usageLog: unknown;
33
+ /** command.json's recorded args, or null when the workdir has none. */
34
+ commandArgs: readonly string[] | null;
35
+ env: NodeJS.ProcessEnv;
36
+ /** Injected existence check (defaultProviderName's own seam) so the
37
+ * matrix needs no PATH scan. */
38
+ hasBin: (bin: string) => boolean;
39
+ }
40
+
41
+ export function captionRegenProvider(inputs: CaptionRegenProviderInputs): CaptionRegenProviderState {
42
+ // The log's shape is user-editable JSON: pick out the two arrays
43
+ // providerOfLog reads and let anything else read as an empty log.
44
+ const log = (inputs.usageLog ?? {}) as { runs?: unknown; records?: unknown };
45
+ const recorded = providerOfLog({
46
+ runs: Array.isArray(log.runs) ? (log.runs as never[]) : [],
47
+ records: Array.isArray(log.records) ? (log.records as never[]) : [],
48
+ });
49
+ const fromLog = ProviderNameSchema.safeParse(recorded);
50
+ if (fromLog.success) return { status: "ready", provider: fromLog.data };
51
+ const fromPin = ProviderNameSchema.safeParse(
52
+ lastFlagValue(inputs.commandArgs ?? [], ["--llm"]),
53
+ );
54
+ if (fromPin.success) return { status: "ready", provider: fromPin.data };
55
+ // defaultProviderName answers "claude-cli" even with nothing installed —
56
+ // deliberate on the CLI, where the spawn failure carries install guidance.
57
+ // A button in a panel deserves the sentence BEFORE the click, so this
58
+ // restates its four detections as an availability gate and only then takes
59
+ // its pick.
60
+ const reachable =
61
+ inputs.hasBin(inputs.env.OSSCLIP_AGY_BIN ?? "agy") ||
62
+ inputs.hasBin(inputs.env.OSSCLIP_CLAUDE_BIN ?? "claude") ||
63
+ Boolean(inputs.env.GEMINI_API_KEY) ||
64
+ Boolean(inputs.env.ANTHROPIC_API_KEY);
65
+ if (!reachable) {
66
+ return {
67
+ status: "unavailable",
68
+ reason:
69
+ "no LLM provider available — set GEMINI_API_KEY or ANTHROPIC_API_KEY in " +
70
+ "~/.ossclip/.env, or install the claude or agy CLI, then try again",
71
+ };
72
+ }
73
+ return { status: "ready", provider: defaultProviderName(inputs.env, inputs.hasBin) };
74
+ }