@gentbajko/slopify 1.0.0 → 1.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.
@@ -3,7 +3,7 @@ import { Hono } from "hono";
3
3
  import { z } from "zod";
4
4
  import { cliPathMaxLength, saveCliPath } from "../../slices/settings/cli-paths.js";
5
5
  import { keyStatus, removeProviderKey, saveProviderKey } from "../../slices/settings/keys.js";
6
- import { providerById, providerIds } from "../../slices/settings/model.js";
6
+ import { isLocalCliProvider, providerById, providerIds } from "../../slices/settings/model.js";
7
7
  import { createModelCatalog } from "../../slices/settings/models.js";
8
8
  import { providerStatuses } from "../../slices/settings/readiness.js";
9
9
  import { onInvalid, problem, titleOf } from "./problem.js";
@@ -46,7 +46,7 @@ export function providerRoutes(deps) {
46
46
  .get("/", async (c) => c.json({ providers: await providerStatuses(readiness) }))
47
47
  .get("/:id/models", zValidator("param", providerParam, onInvalid), async (c) => {
48
48
  const { id } = c.req.valid("param");
49
- if (deps.catalogue)
49
+ if (deps.catalogue && !isLocalCliProvider(id))
50
50
  return c.json({
51
51
  models: deps.catalogue.models(id, providerById(id).family).map((m) => ({
52
52
  ...m,
package/dist/main.js CHANGED
@@ -43,6 +43,7 @@ import { executionStages, executionStandings, invocationReady, projectStandings,
43
43
  import { settleTerminalScheduleRuns } from "./slices/schedules/repo.js";
44
44
  import { createScheduleRunner } from "./slices/schedules/scheduler.js";
45
45
  import { nodeCliProbe } from "./slices/settings/cli-status.js";
46
+ import { isLocalCliProvider } from "./slices/settings/model.js";
46
47
  import { providerStatuses } from "./slices/settings/readiness.js";
47
48
  import { reconcileStorage } from "./slices/storage/reconcile.js";
48
49
  import { collectorEndpoint, httpPostEvents } from "./slices/telemetry/collector-client.js";
@@ -407,7 +408,7 @@ export function wireRunner({ audioPreviews, db, paths, clock, ids, log, hub, tel
407
408
  attempts: sqliteAttempts(db, ids),
408
409
  clock,
409
410
  log,
410
- queue: createProviderQueue((provider) => catalogue.read().providers[provider]?.maxConcurrent ?? 1),
411
+ queue: createProviderQueue((provider) => isLocalCliProvider(provider) ? 3 : (catalogue.read().providers[provider]?.maxConcurrent ?? 1)),
411
412
  };
412
413
  const checkpoints = createCheckpointAuthority({
413
414
  decide: (work) => checkpointDecisionForWork(execution, work),
@@ -2,9 +2,6 @@ import { falModels } from "./adapters/image/fal.js";
2
2
  import { googleImageModels } from "./adapters/image/google.js";
3
3
  import { openAiImageModels } from "./adapters/image/openai.js";
4
4
  import { replicateModels } from "./adapters/image/replicate.js";
5
- import { claudeCodeModels } from "./adapters/llm/claude-code.js";
6
- import { codexModels } from "./adapters/llm/codex.js";
7
- import { geminiModels } from "./adapters/llm/gemini.js";
8
5
  import { cartesiaModels } from "./adapters/tts/cartesia.js";
9
6
  import { elevenLabsModels } from "./adapters/tts/elevenlabs.js";
10
7
  import { inworldModels } from "./adapters/tts/inworld.js";
@@ -12,9 +9,6 @@ import { openAiTtsModels } from "./adapters/tts/openai.js";
12
9
  // Composition keeps provider-specific fallback data out of the HTTP and settings layers.
13
10
  export function modelSources(registry) {
14
11
  const fallbacks = {
15
- "claude-code": claudeCodeModels,
16
- codex: codexModels,
17
- gemini: geminiModels,
18
12
  elevenlabs: elevenLabsModels,
19
13
  "openai-tts": openAiTtsModels,
20
14
  cartesia: cartesiaModels,
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { checkRuntimeModel } from "../../catalog/runtime-models.js";
2
3
  import { thinkingModes } from "../../kernel/ports/llm.js";
3
4
  import { readinessIsUsable, } from "../../kernel/ports/model.js";
4
5
  import { chunkModes } from "../narration/chunk.js";
@@ -64,14 +65,11 @@ export async function validateProviderChanges(changes, current) {
64
65
  continue;
65
66
  if (current.allowsCustomModels?.(picked.provider) === true)
66
67
  continue;
67
- const models = await current.modelsFor(picked.provider, families[key]);
68
- const model = models.find((model) => model.id === picked.model);
69
- if (!model) {
68
+ const result = await checkRuntimeModel(current.modelsFor, picked.provider, families[key], picked.model, picked.thinking);
69
+ if (result === "missing") {
70
70
  fields.push({ field: `${key}.model`, message: "Choose a model supported by this provider." });
71
71
  }
72
- else if (key === "llm" &&
73
- picked.thinking &&
74
- !model.thinkingModes?.includes(picked.thinking)) {
72
+ else if (result === "thinking") {
75
73
  fields.push({ field: `${key}.thinking`, message: "Choose a supported thinking setting." });
76
74
  }
77
75
  }
@@ -1,8 +1,9 @@
1
+ import { checkRuntimeModel } from "../../catalog/runtime-models.js";
1
2
  import { modelFields } from "../../catalog/validate.js";
2
3
  import { readinessIsUsable } from "../../kernel/ports/model.js";
3
4
  import { cliPathStatus } from "../settings/cli-paths.js";
4
5
  import { hasKey, listVoices } from "../settings/repo.js";
5
- function choices(runs) {
6
+ export function choices(runs) {
6
7
  const selected = runs
7
8
  .flatMap(({ draft: d }) => [
8
9
  {
@@ -49,12 +50,17 @@ export async function checkDraftReadiness(deps, runs) {
49
50
  });
50
51
  continue;
51
52
  }
52
- const models = await deps.modelsFor(c.provider, c.family);
53
- if (!models.some((m) => m.id === c.model))
53
+ const result = await checkRuntimeModel(deps.modelsFor, c.provider, c.family, c.model, c.thinking);
54
+ if (result === "missing")
54
55
  fields.push({
55
56
  field: `${c.field}.model`,
56
57
  message: "Choose an available model before starting.",
57
58
  });
59
+ else if (result === "thinking")
60
+ fields.push({
61
+ field: `${c.field}.thinking`,
62
+ message: "Choose a supported thinking setting.",
63
+ });
58
64
  }
59
65
  return { fields: [...fields, ...localDraftReadiness(deps, runs, providers)], providers };
60
66
  }
@@ -1,6 +1,8 @@
1
1
  import { z } from "zod";
2
+ import { checkRuntimeModel } from "../../catalog/runtime-models.js";
2
3
  import { transact } from "../../kernel/db/tx.js";
3
4
  import { isMissingFont } from "../fonts/index.js";
5
+ import { choices } from "./readiness.js";
4
6
  import { draftRow } from "./repo.js";
5
7
  import { resolveReviewInputs, reviewBinding, reviewRefusal } from "./review-inputs.js";
6
8
  import { draftViewSchema, playReviewSchema } from "./schema.js";
@@ -33,6 +35,22 @@ export async function resolvePlayReview(deps, view) {
33
35
  const after = resolveReviewInputs(deps, parsed.data, font);
34
36
  if (!after.ok)
35
37
  return after;
38
+ const fields = [];
39
+ for (const choice of choices(after.value.runs)) {
40
+ const result = await checkRuntimeModel(deps.modelsFor, choice.provider, choice.family, choice.model, choice.thinking);
41
+ if (result === "missing")
42
+ fields.push({
43
+ field: `${choice.field}.model`,
44
+ message: "Choose an available model before reviewing.",
45
+ });
46
+ else if (result === "thinking")
47
+ fields.push({
48
+ field: `${choice.field}.thinking`,
49
+ message: "Choose a supported thinking setting.",
50
+ });
51
+ }
52
+ if (fields.length)
53
+ return reviewRefusal(view, fields);
36
54
  return reviewBinding(before.value) === reviewBinding(after.value)
37
55
  ? after
38
56
  : reviewRefusal(view, [], "stale-review");
@@ -1,5 +1,7 @@
1
+ import { checkRuntimeModel } from "../../catalog/runtime-models.js";
1
2
  import { readinessIsUsable } from "../../kernel/ports/model.js";
2
3
  import { cliPathStatus } from "../settings/cli-paths.js";
4
+ import { isLocalCliProvider } from "../settings/model.js";
3
5
  import { hasKey, listVoices } from "../settings/repo.js";
4
6
  import { requiresNewSubmission } from "./preview-retained.js";
5
7
  import { recipeProviderChoice } from "./recipe-provider-choice.js";
@@ -36,12 +38,17 @@ export async function checkReadiness(deps, snapshot, view) {
36
38
  });
37
39
  continue;
38
40
  }
39
- const models = await deps.modelsFor(choice.provider, choice.family);
40
- if (!models.some((row) => row.id === choice.model))
41
+ const result = await checkRuntimeModel(deps.modelsFor, choice.provider, choice.family, choice.model, choice.family === "llm" ? (choice.thinking ?? undefined) : undefined);
42
+ if (result === "missing")
41
43
  fields.push({
42
44
  field: `${choice.family}.model`,
43
45
  message: "Choose an available model before rebuilding.",
44
46
  });
47
+ else if (result === "thinking")
48
+ fields.push({
49
+ field: `${choice.family}.thinking`,
50
+ message: "Choose a supported thinking setting.",
51
+ });
45
52
  }
46
53
  return { fields, providers };
47
54
  }
@@ -52,7 +59,7 @@ export function localReadiness(deps, snapshot, view, providers, catalogue) {
52
59
  row.id === choice.model &&
53
60
  row.enabled &&
54
61
  !row.deprecated);
55
- if (model === undefined)
62
+ if (model === undefined && !isLocalCliProvider(choice.provider))
56
63
  fields.push({
57
64
  field: `${choice.family}.model`,
58
65
  message: "The selected model is no longer available.",
@@ -85,6 +92,8 @@ export function localReadiness(deps, snapshot, view, providers, catalogue) {
85
92
  });
86
93
  }
87
94
  else if (input.kind === "llm") {
95
+ if (isLocalCliProvider(input.provider))
96
+ continue;
88
97
  const model = catalogue.llm.find((row) => row.provider === input.provider && row.id === input.model);
89
98
  if (input.thinking !== null && model?.llm.thinking?.[input.thinking] === undefined)
90
99
  fields.push({
@@ -18,6 +18,9 @@ export const providerIds = [
18
18
  "openai-image",
19
19
  "google-image",
20
20
  ];
21
+ export function isLocalCliProvider(id) {
22
+ return id === "claude-code" || id === "codex" || id === "gemini" || id === "codex-image";
23
+ }
21
24
  export const providers = [
22
25
  { id: "openrouter", family: "llm", displayName: "OpenRouter", auth: "key" },
23
26
  {
@@ -1,7 +1,8 @@
1
+ import { isLocalCliProvider } from "./model.js";
1
2
  // These marketplaces host models with different input schemas. Discovery alone cannot
2
3
  // make an arbitrary endpoint compatible with our image request.
3
4
  export function allowsCustomModel(provider) {
4
- return provider !== "fal" && provider !== "replicate";
5
+ return provider !== "fal" && provider !== "replicate" && provider !== "codex-image";
5
6
  }
6
7
  const ttlMs = 5 * 60_000;
7
8
  const retryMs = 30_000;
@@ -33,14 +34,19 @@ export function createModelCatalog(deps) {
33
34
  // A catalogue is metadata, not generation. Never expose an upstream response
34
35
  // here: it can quote credentials that belong only inside its adapter.
35
36
  deps.report(provider);
36
- const saved = previous?.value.models;
37
+ const local = isLocalCliProvider(provider);
38
+ const saved = local ? undefined : previous?.value.models;
37
39
  value = {
38
- models: saved ?? deps.fallback(provider),
40
+ models: local ? [] : (saved ?? deps.fallback(provider)),
39
41
  ...catalogNotice(provider),
40
42
  allowsCustom: allowsCustomModel(provider),
41
- warning: saved === undefined
42
- ? "Could not load the model list. Showing bundled choices; refresh to try again."
43
- : "Could not refresh the model list. Showing the last loaded choices.",
43
+ warning: local
44
+ ? provider === "codex-image"
45
+ ? "Could not check the Codex image capability. Refresh after checking the CLI."
46
+ : "Could not discover models from this CLI. Enter an exact model ID manually or refresh."
47
+ : saved === undefined
48
+ ? "Could not load the model list. Showing bundled choices; refresh to try again."
49
+ : "Could not refresh the model list. Showing the last loaded choices.",
44
50
  };
45
51
  lifetime = retryMs;
46
52
  }