@chances-ai/cli 2.0.0 → 3.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/dist/boot/create-app.d.ts +3 -1
- package/dist/boot/create-app.d.ts.map +1 -1
- package/dist/boot/create-app.js +4 -3
- package/dist/boot/create-app.js.map +1 -1
- package/dist/boot/load-runtime.d.ts +5 -1
- package/dist/boot/load-runtime.d.ts.map +1 -1
- package/dist/boot/load-runtime.js +17 -2
- package/dist/boot/load-runtime.js.map +1 -1
- package/dist/commands/chat.d.ts +9 -1
- package/dist/commands/chat.d.ts.map +1 -1
- package/dist/commands/chat.js +95 -39
- package/dist/commands/chat.js.map +1 -1
- package/dist/commands/prompt.d.ts.map +1 -1
- package/dist/commands/prompt.js +30 -5
- package/dist/commands/prompt.js.map +1 -1
- package/dist/slash/clear.d.ts +13 -0
- package/dist/slash/clear.d.ts.map +1 -0
- package/dist/slash/clear.js +28 -0
- package/dist/slash/clear.js.map +1 -0
- package/dist/slash/compact.d.ts +4 -0
- package/dist/slash/compact.d.ts.map +1 -0
- package/dist/slash/compact.js +68 -0
- package/dist/slash/compact.js.map +1 -0
- package/dist/slash/help.d.ts +13 -0
- package/dist/slash/help.d.ts.map +1 -0
- package/dist/slash/help.js +34 -0
- package/dist/slash/help.js.map +1 -0
- package/dist/slash/index.d.ts +22 -0
- package/dist/slash/index.d.ts.map +1 -0
- package/dist/slash/index.js +20 -0
- package/dist/slash/index.js.map +1 -0
- package/dist/slash/logout.d.ts +15 -0
- package/dist/slash/logout.d.ts.map +1 -0
- package/dist/slash/logout.js +44 -0
- package/dist/slash/logout.js.map +1 -0
- package/dist/slash/model.d.ts +14 -0
- package/dist/slash/model.d.ts.map +1 -0
- package/dist/slash/model.js +87 -0
- package/dist/slash/model.js.map +1 -0
- package/dist/slash/persist.d.ts +16 -0
- package/dist/slash/persist.d.ts.map +1 -0
- package/dist/slash/persist.js +46 -0
- package/dist/slash/persist.js.map +1 -0
- package/dist/slash/provider.d.ts +17 -0
- package/dist/slash/provider.d.ts.map +1 -0
- package/dist/slash/provider.js +98 -0
- package/dist/slash/provider.js.map +1 -0
- package/dist/slash/resume.d.ts +15 -0
- package/dist/slash/resume.d.ts.map +1 -0
- package/dist/slash/resume.js +76 -0
- package/dist/slash/resume.js.map +1 -0
- package/package.json +13 -13
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { makeHelpPlugin } from "./help.js";
|
|
2
|
+
import { makeModelPlugin } from "./model.js";
|
|
3
|
+
import { makeProviderPlugin } from "./provider.js";
|
|
4
|
+
import { makeClearPlugin } from "./clear.js";
|
|
5
|
+
import { makeResumePlugin } from "./resume.js";
|
|
6
|
+
import { makeCompactPlugin } from "./compact.js";
|
|
7
|
+
import { makeLogoutPlugin } from "./logout.js";
|
|
8
|
+
export async function registerBuiltinSlashCommands(host, deps) {
|
|
9
|
+
// Order doesn't matter; each plugin only mutates its own command slot.
|
|
10
|
+
// `await` so any onLoad error (e.g. duplicate registration in a buggy
|
|
11
|
+
// teardown) surfaces immediately rather than as an unhandled rejection.
|
|
12
|
+
await host.load(makeHelpPlugin(host));
|
|
13
|
+
await host.load(makeModelPlugin(deps));
|
|
14
|
+
await host.load(makeProviderPlugin(deps));
|
|
15
|
+
await host.load(makeClearPlugin(deps));
|
|
16
|
+
await host.load(makeResumePlugin(deps));
|
|
17
|
+
await host.load(makeCompactPlugin(deps));
|
|
18
|
+
await host.load(makeLogoutPlugin(deps));
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/slash/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAmB/C,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAChD,IAAgB,EAChB,IAAsB;IAEtB,uEAAuE;IACvE,sEAAsE;IACtE,wEAAwE;IACxE,MAAM,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;IACtC,MAAM,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC;IACvC,MAAM,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC;IAC1C,MAAM,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC;IACvC,MAAM,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC;IACxC,MAAM,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC;IACzC,MAAM,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC;AAC1C,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type Plugin } from "@chances-ai/plugin-api";
|
|
2
|
+
import type { BuiltinSlashDeps } from "./index.js";
|
|
3
|
+
/**
|
|
4
|
+
* `/logout <provider>` — removes a provider's stored API key from the auth
|
|
5
|
+
* file. Does NOT touch env vars (we have no way to unset a parent shell's
|
|
6
|
+
* env). If the active selection points at the logged-out provider, clears
|
|
7
|
+
* the provider preference so the router falls back to the cheapest credentialed
|
|
8
|
+
* option on the next turn instead of failing.
|
|
9
|
+
*
|
|
10
|
+
* No interactive form yet — argument is required. A `/logout` with no arg
|
|
11
|
+
* could either log out everything (dangerous) or open a picker (more UI); we
|
|
12
|
+
* defer both until a real need.
|
|
13
|
+
*/
|
|
14
|
+
export declare function makeLogoutPlugin(deps: BuiltinSlashDeps): Plugin;
|
|
15
|
+
//# sourceMappingURL=logout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logout.d.ts","sourceRoot":"","sources":["../../src/slash/logout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,MAAM,EAAqB,MAAM,wBAAwB,CAAC;AAEtF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,CA8B/D"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { definePlugin } from "@chances-ai/plugin-api";
|
|
2
|
+
import { KNOWN_MODELS } from "@chances-ai/ai";
|
|
3
|
+
/**
|
|
4
|
+
* `/logout <provider>` — removes a provider's stored API key from the auth
|
|
5
|
+
* file. Does NOT touch env vars (we have no way to unset a parent shell's
|
|
6
|
+
* env). If the active selection points at the logged-out provider, clears
|
|
7
|
+
* the provider preference so the router falls back to the cheapest credentialed
|
|
8
|
+
* option on the next turn instead of failing.
|
|
9
|
+
*
|
|
10
|
+
* No interactive form yet — argument is required. A `/logout` with no arg
|
|
11
|
+
* could either log out everything (dangerous) or open a picker (more UI); we
|
|
12
|
+
* defer both until a real need.
|
|
13
|
+
*/
|
|
14
|
+
export function makeLogoutPlugin(deps) {
|
|
15
|
+
const command = {
|
|
16
|
+
name: "logout",
|
|
17
|
+
description: "Remove a stored provider credential.",
|
|
18
|
+
run: (args) => {
|
|
19
|
+
const target = args[0];
|
|
20
|
+
if (!target)
|
|
21
|
+
return "usage: /logout <provider>";
|
|
22
|
+
const known = KNOWN_MODELS.find((e) => e.id === target);
|
|
23
|
+
if (!known)
|
|
24
|
+
return `unknown provider: ${target}. /provider list shows what's known.`;
|
|
25
|
+
// Idempotent on the auth file — deleteKey is a no-op when the key
|
|
26
|
+
// isn't there. We still adjust selection because the env var may have
|
|
27
|
+
// been the source, and the user's intent is "stop using this provider".
|
|
28
|
+
deps.ctx.auth.deleteKey(target);
|
|
29
|
+
const current = deps.ctx.selection.get();
|
|
30
|
+
if (current.provider === target) {
|
|
31
|
+
deps.ctx.selection.set({ provider: undefined, model: undefined });
|
|
32
|
+
return `${target} logged out — selection cleared so the router will pick the cheapest available next turn`;
|
|
33
|
+
}
|
|
34
|
+
return `${target} logged out (no env var unset — clear ${known.envKey} in your shell if it was set there)`;
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
return definePlugin({
|
|
38
|
+
name: "builtin/logout",
|
|
39
|
+
onLoad(ctx) {
|
|
40
|
+
ctx.registerSlashCommand(command);
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=logout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logout.js","sourceRoot":"","sources":["../../src/slash/logout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAkC,MAAM,wBAAwB,CAAC;AACtF,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAG9C;;;;;;;;;;GAUG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAsB;IACrD,MAAM,OAAO,GAAiB;QAC5B,IAAI,EAAE,QAAQ;QACd,WAAW,EAAE,sCAAsC;QACnD,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE;YACZ,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YACvB,IAAI,CAAC,MAAM;gBAAE,OAAO,2BAA2B,CAAC;YAEhD,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC;YACxD,IAAI,CAAC,KAAK;gBAAE,OAAO,qBAAqB,MAAM,sCAAsC,CAAC;YAErF,kEAAkE;YAClE,sEAAsE;YACtE,wEAAwE;YACxE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;YAEhC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC;YACzC,IAAI,OAAO,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;gBAChC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;gBAClE,OAAO,GAAG,MAAM,0FAA0F,CAAC;YAC7G,CAAC;YACD,OAAO,GAAG,MAAM,yCAAyC,KAAK,CAAC,MAAM,qCAAqC,CAAC;QAC7G,CAAC;KACF,CAAC;IACF,OAAO,YAAY,CAAC;QAClB,IAAI,EAAE,gBAAgB;QACtB,MAAM,CAAC,GAAG;YACR,GAAG,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC;QACpC,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type Plugin } from "@chances-ai/plugin-api";
|
|
2
|
+
import type { BuiltinSlashDeps } from "./index.js";
|
|
3
|
+
/**
|
|
4
|
+
* `/model [id]`
|
|
5
|
+
* - With an id: switches the active model (matches by `id` alone or by
|
|
6
|
+
* `provider/id`). Writes the choice to `<workspaceRoot>/.chances/config.json`
|
|
7
|
+
* so the next invocation in this workspace defaults to it (per design call
|
|
8
|
+
* "default persisted").
|
|
9
|
+
* - Without args in TUI mode: opens the model picker (Step 6's ModelPicker).
|
|
10
|
+
* - Without args in text mode: prints the available models, marking the
|
|
11
|
+
* current one. Aligns with oh-my-pi's text-mode model list.
|
|
12
|
+
*/
|
|
13
|
+
export declare function makeModelPlugin(deps: BuiltinSlashDeps): Plugin;
|
|
14
|
+
//# sourceMappingURL=model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/slash/model.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,MAAM,EAAqB,MAAM,wBAAwB,CAAC;AAGtF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGnD;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,CAwC9D"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { createElement } from "react";
|
|
2
|
+
import { definePlugin } from "@chances-ai/plugin-api";
|
|
3
|
+
import { ModelPicker } from "@chances-ai/tui";
|
|
4
|
+
import { persistProjectModelChoice } from "./persist.js";
|
|
5
|
+
/**
|
|
6
|
+
* `/model [id]`
|
|
7
|
+
* - With an id: switches the active model (matches by `id` alone or by
|
|
8
|
+
* `provider/id`). Writes the choice to `<workspaceRoot>/.chances/config.json`
|
|
9
|
+
* so the next invocation in this workspace defaults to it (per design call
|
|
10
|
+
* "default persisted").
|
|
11
|
+
* - Without args in TUI mode: opens the model picker (Step 6's ModelPicker).
|
|
12
|
+
* - Without args in text mode: prints the available models, marking the
|
|
13
|
+
* current one. Aligns with oh-my-pi's text-mode model list.
|
|
14
|
+
*/
|
|
15
|
+
export function makeModelPlugin(deps) {
|
|
16
|
+
const command = {
|
|
17
|
+
name: "model",
|
|
18
|
+
description: "Show or switch the active model.",
|
|
19
|
+
run: async (args, slashCtx) => {
|
|
20
|
+
const all = deps.ctx.registry.all();
|
|
21
|
+
const current = deps.ctx.selection.get();
|
|
22
|
+
if (args.length === 0) {
|
|
23
|
+
if (slashCtx.openModal) {
|
|
24
|
+
// TUI mode: open the picker. The picker is intentionally rendered
|
|
25
|
+
// inside the chat UI via the modal slot, not as a separate process.
|
|
26
|
+
const picked = await slashCtx.openModal((resolve) => {
|
|
27
|
+
const items = all.map((m) => ({
|
|
28
|
+
id: m.id,
|
|
29
|
+
provider: m.provider,
|
|
30
|
+
current: m.id === current.model && m.provider === current.provider,
|
|
31
|
+
}));
|
|
32
|
+
return renderModelPicker(items, resolve);
|
|
33
|
+
});
|
|
34
|
+
if (!picked)
|
|
35
|
+
return "model unchanged";
|
|
36
|
+
return applyChoice(deps, picked.provider, picked.id);
|
|
37
|
+
}
|
|
38
|
+
// Text mode: list the models the registry knows about.
|
|
39
|
+
return listText(all, current.model, current.provider);
|
|
40
|
+
}
|
|
41
|
+
// Args path. Accept either "id" or "provider/id" — pi's parseModelId pattern.
|
|
42
|
+
const want = args[0];
|
|
43
|
+
const match = resolveModel(all, want);
|
|
44
|
+
if (!match)
|
|
45
|
+
return `unknown model: ${want}. Run /model to see available ids.`;
|
|
46
|
+
return applyChoice(deps, match.provider, match.id);
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
return definePlugin({
|
|
50
|
+
name: "builtin/model",
|
|
51
|
+
onLoad(ctx) {
|
|
52
|
+
ctx.registerSlashCommand(command);
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
function applyChoice(deps, provider, model) {
|
|
57
|
+
deps.ctx.selection.set({ provider, model });
|
|
58
|
+
deps.session.setModel({ provider, model });
|
|
59
|
+
const persisted = persistProjectModelChoice(deps.ctx.workspaceRoot, { provider, model });
|
|
60
|
+
if (!persisted) {
|
|
61
|
+
return `model set to ${provider}/${model} (this session only — could not write .chances/config.json)`;
|
|
62
|
+
}
|
|
63
|
+
return `model set to ${provider}/${model} (saved to .chances/config.json)`;
|
|
64
|
+
}
|
|
65
|
+
function resolveModel(models, want) {
|
|
66
|
+
return models.find((m) => m.id === want || `${m.provider}/${m.id}` === want);
|
|
67
|
+
}
|
|
68
|
+
function listText(models, curModel, curProvider) {
|
|
69
|
+
if (models.length === 0)
|
|
70
|
+
return "no models registered — check your config and API keys";
|
|
71
|
+
const padTo = Math.max(...models.map((m) => `${m.provider}/${m.id}`.length));
|
|
72
|
+
const lines = models.map((m) => {
|
|
73
|
+
const active = m.id === curModel && m.provider === curProvider;
|
|
74
|
+
const marker = active ? "● " : " ";
|
|
75
|
+
return `${marker}${`${m.provider}/${m.id}`.padEnd(padTo)} ctx=${m.contextWindow}`;
|
|
76
|
+
});
|
|
77
|
+
return ["Available models (● = current):", ...lines].join("\n");
|
|
78
|
+
}
|
|
79
|
+
/** Builds the React element for the picker. The `openModal` host expects
|
|
80
|
+
* `unknown`, but we know the concrete shape — return `createElement(...)`
|
|
81
|
+
* which Ink will render. Even in text mode this code path isn't reached
|
|
82
|
+
* (the dispatcher leaves openModal undefined), so importing the React + Ink
|
|
83
|
+
* components at module top is free of runtime cost there. */
|
|
84
|
+
function renderModelPicker(items, resolve) {
|
|
85
|
+
return createElement(ModelPicker, { models: items, onChoose: resolve });
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/slash/model.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Writes the user's model/provider choice into `<workspaceRoot>/.chances/config.json`
|
|
3
|
+
* so the next `chances` invocation in this workspace defaults to it. Preserves
|
|
4
|
+
* any unrelated fields (permissions, plugins, …) — only the `provider` and
|
|
5
|
+
* `model` keys are touched.
|
|
6
|
+
*
|
|
7
|
+
* Returns true on success, false on filesystem failure (so the calling slash
|
|
8
|
+
* command can degrade to "in-session only" without throwing). Failures are
|
|
9
|
+
* intentionally not logged here — the slash command surfaces a user-visible
|
|
10
|
+
* notice with the right context.
|
|
11
|
+
*/
|
|
12
|
+
export declare function persistProjectModelChoice(workspaceRoot: string, choice: {
|
|
13
|
+
provider?: string;
|
|
14
|
+
model?: string;
|
|
15
|
+
}): boolean;
|
|
16
|
+
//# sourceMappingURL=persist.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persist.d.ts","sourceRoot":"","sources":["../../src/slash/persist.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;GAUG;AACH,wBAAgB,yBAAyB,CACvC,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5C,OAAO,CA2BT"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Writes the user's model/provider choice into `<workspaceRoot>/.chances/config.json`
|
|
5
|
+
* so the next `chances` invocation in this workspace defaults to it. Preserves
|
|
6
|
+
* any unrelated fields (permissions, plugins, …) — only the `provider` and
|
|
7
|
+
* `model` keys are touched.
|
|
8
|
+
*
|
|
9
|
+
* Returns true on success, false on filesystem failure (so the calling slash
|
|
10
|
+
* command can degrade to "in-session only" without throwing). Failures are
|
|
11
|
+
* intentionally not logged here — the slash command surfaces a user-visible
|
|
12
|
+
* notice with the right context.
|
|
13
|
+
*/
|
|
14
|
+
export function persistProjectModelChoice(workspaceRoot, choice) {
|
|
15
|
+
const dir = join(workspaceRoot, ".chances");
|
|
16
|
+
const path = join(dir, "config.json");
|
|
17
|
+
try {
|
|
18
|
+
mkdirSync(dir, { recursive: true });
|
|
19
|
+
let existing = {};
|
|
20
|
+
if (existsSync(path)) {
|
|
21
|
+
try {
|
|
22
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
23
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
24
|
+
existing = parsed;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// Malformed file — start fresh rather than refusing to write. The
|
|
29
|
+
// user's choice should win over a stale parse failure they may not
|
|
30
|
+
// even know about.
|
|
31
|
+
existing = {};
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const next = { ...existing };
|
|
35
|
+
if (choice.provider !== undefined)
|
|
36
|
+
next.provider = choice.provider;
|
|
37
|
+
if (choice.model !== undefined)
|
|
38
|
+
next.model = choice.model;
|
|
39
|
+
writeFileSync(path, JSON.stringify(next, null, 2) + "\n");
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=persist.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persist.js","sourceRoot":"","sources":["../../src/slash/persist.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC;;;;;;;;;;GAUG;AACH,MAAM,UAAU,yBAAyB,CACvC,aAAqB,EACrB,MAA6C;IAE7C,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC;IAC5C,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;IACtC,IAAI,CAAC;QACH,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACpC,IAAI,QAAQ,GAA4B,EAAE,CAAC;QAC3C,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACrB,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAY,CAAC;gBACjE,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;oBACnE,QAAQ,GAAG,MAAiC,CAAC;gBAC/C,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,kEAAkE;gBAClE,mEAAmE;gBACnE,mBAAmB;gBACnB,QAAQ,GAAG,EAAE,CAAC;YAChB,CAAC;QACH,CAAC;QACD,MAAM,IAAI,GAA4B,EAAE,GAAG,QAAQ,EAAE,CAAC;QACtD,IAAI,MAAM,CAAC,QAAQ,KAAK,SAAS;YAAE,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;QACnE,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS;YAAE,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;QAC1D,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QAC1D,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type Plugin } from "@chances-ai/plugin-api";
|
|
2
|
+
import type { BuiltinSlashDeps } from "./index.js";
|
|
3
|
+
/**
|
|
4
|
+
* `/provider [list|use|add]`
|
|
5
|
+
*
|
|
6
|
+
* - `/provider list` — text dump of which providers are configured.
|
|
7
|
+
* - `/provider use <id>` — sets selection's provider; lets the router pick
|
|
8
|
+
* the first model of that provider for next turn.
|
|
9
|
+
* - `/provider add <id>` — TUI: opens ApiKeyPrompt to enter the key, then
|
|
10
|
+
* persists via AuthStore.setKey. Text mode: refuses
|
|
11
|
+
* with an env-var hint (matching pi's pattern:
|
|
12
|
+
* `<ENVKEY>=… chances` works headlessly).
|
|
13
|
+
*
|
|
14
|
+
* Default (no subcommand) is `list`.
|
|
15
|
+
*/
|
|
16
|
+
export declare function makeProviderPlugin(deps: BuiltinSlashDeps): Plugin;
|
|
17
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/slash/provider.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,MAAM,EAAqB,MAAM,wBAAwB,CAAC;AAGtF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,CA2BjE"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { createElement } from "react";
|
|
2
|
+
import { definePlugin } from "@chances-ai/plugin-api";
|
|
3
|
+
import { KNOWN_MODELS } from "@chances-ai/ai";
|
|
4
|
+
import { ApiKeyPrompt } from "@chances-ai/tui";
|
|
5
|
+
/**
|
|
6
|
+
* `/provider [list|use|add]`
|
|
7
|
+
*
|
|
8
|
+
* - `/provider list` — text dump of which providers are configured.
|
|
9
|
+
* - `/provider use <id>` — sets selection's provider; lets the router pick
|
|
10
|
+
* the first model of that provider for next turn.
|
|
11
|
+
* - `/provider add <id>` — TUI: opens ApiKeyPrompt to enter the key, then
|
|
12
|
+
* persists via AuthStore.setKey. Text mode: refuses
|
|
13
|
+
* with an env-var hint (matching pi's pattern:
|
|
14
|
+
* `<ENVKEY>=… chances` works headlessly).
|
|
15
|
+
*
|
|
16
|
+
* Default (no subcommand) is `list`.
|
|
17
|
+
*/
|
|
18
|
+
export function makeProviderPlugin(deps) {
|
|
19
|
+
const command = {
|
|
20
|
+
name: "provider",
|
|
21
|
+
description: "List / switch / add provider credentials.",
|
|
22
|
+
run: async (args, slashCtx) => {
|
|
23
|
+
const sub = args[0] ?? "list";
|
|
24
|
+
if (sub === "list")
|
|
25
|
+
return runList(deps);
|
|
26
|
+
if (sub === "use") {
|
|
27
|
+
const target = args[1];
|
|
28
|
+
if (!target)
|
|
29
|
+
return "usage: /provider use <id>";
|
|
30
|
+
return runUse(deps, target);
|
|
31
|
+
}
|
|
32
|
+
if (sub === "add") {
|
|
33
|
+
const target = args[1];
|
|
34
|
+
if (!target)
|
|
35
|
+
return "usage: /provider add <id>";
|
|
36
|
+
return runAdd(deps, target, slashCtx.openModal);
|
|
37
|
+
}
|
|
38
|
+
return `unknown subcommand: ${sub} (try: list, use, add)`;
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
return definePlugin({
|
|
42
|
+
name: "builtin/provider",
|
|
43
|
+
onLoad(ctx) {
|
|
44
|
+
ctx.registerSlashCommand(command);
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
function runList(deps) {
|
|
49
|
+
const current = deps.ctx.selection.get();
|
|
50
|
+
const lines = KNOWN_MODELS.map((entry) => {
|
|
51
|
+
const has = deps.ctx.auth.hasCredentials(entry.id);
|
|
52
|
+
const active = entry.id === current.provider;
|
|
53
|
+
const marker = active ? "●" : has ? "✓" : "·";
|
|
54
|
+
return ` ${marker} ${entry.id.padEnd(12)} ${has ? "configured" : `set ${entry.envKey}`}`;
|
|
55
|
+
});
|
|
56
|
+
return [
|
|
57
|
+
"Providers (● = active, ✓ = configured, · = no credential):",
|
|
58
|
+
...lines,
|
|
59
|
+
].join("\n");
|
|
60
|
+
}
|
|
61
|
+
function runUse(deps, target) {
|
|
62
|
+
const entry = KNOWN_MODELS.find((e) => e.id === target);
|
|
63
|
+
if (!entry)
|
|
64
|
+
return `unknown provider: ${target}. /provider list shows what's known.`;
|
|
65
|
+
if (!deps.ctx.auth.hasCredentials(target)) {
|
|
66
|
+
return `provider ${target} has no credential — run /provider add ${target} or set ${entry.envKey}`;
|
|
67
|
+
}
|
|
68
|
+
deps.ctx.selection.set({ provider: target, model: undefined });
|
|
69
|
+
return `provider set to ${target} (router will pick a default model from this provider next turn)`;
|
|
70
|
+
}
|
|
71
|
+
async function runAdd(deps, target, openModal) {
|
|
72
|
+
const entry = KNOWN_MODELS.find((e) => e.id === target);
|
|
73
|
+
if (!entry)
|
|
74
|
+
return `unknown provider: ${target}. /provider list shows what's known.`;
|
|
75
|
+
if (!openModal) {
|
|
76
|
+
// Text/`-p` mode: we can't safely prompt for a credential with no TTY.
|
|
77
|
+
// Direct the user to the env-var path (pi's `getEnvApiKey` pattern —
|
|
78
|
+
// already wired in our AuthStore via envByProvider).
|
|
79
|
+
return [
|
|
80
|
+
`interactive credential entry is TUI-only.`,
|
|
81
|
+
`set ${entry.envKey}=<your-key> in your shell and re-run, or run /provider add ${target} from the interactive chat.`,
|
|
82
|
+
].join("\n");
|
|
83
|
+
}
|
|
84
|
+
const key = await openModal((resolve) => renderApiKeyPrompt(target, resolve));
|
|
85
|
+
if (!key)
|
|
86
|
+
return `cancelled — no credential written for ${target}`;
|
|
87
|
+
try {
|
|
88
|
+
deps.ctx.auth.setKey(target, key);
|
|
89
|
+
}
|
|
90
|
+
catch (e) {
|
|
91
|
+
return `failed to write credential: ${e.message}`;
|
|
92
|
+
}
|
|
93
|
+
return `credential saved for ${target} (~/.config/chances/auth.json, 0600). Use /provider use ${target} to switch.`;
|
|
94
|
+
}
|
|
95
|
+
function renderApiKeyPrompt(provider, resolve) {
|
|
96
|
+
return createElement(ApiKeyPrompt, { provider, onSubmit: resolve });
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/slash/provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type Plugin } from "@chances-ai/plugin-api";
|
|
2
|
+
import type { BuiltinSlashDeps } from "./index.js";
|
|
3
|
+
/**
|
|
4
|
+
* `/resume [id]`
|
|
5
|
+
* - With an id: triggers a respawn that re-enters the chat loop with the
|
|
6
|
+
* specified session. Unknown id → error.
|
|
7
|
+
* - Without args in TUI: opens SessionPicker (last 10 by updatedAt).
|
|
8
|
+
* - Without args in text mode: lists session ids the user can pass.
|
|
9
|
+
*
|
|
10
|
+
* The actual session-swap is delegated to the chat loop via `deps.respawn`.
|
|
11
|
+
* The slash command itself doesn't mutate the live engine — that would leave
|
|
12
|
+
* the SessionManager reference dangling. Step 8 wires the respawn loop.
|
|
13
|
+
*/
|
|
14
|
+
export declare function makeResumePlugin(deps: BuiltinSlashDeps): Plugin;
|
|
15
|
+
//# sourceMappingURL=resume.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resume.d.ts","sourceRoot":"","sources":["../../src/slash/resume.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,MAAM,EAAqB,MAAM,wBAAwB,CAAC;AAEtF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,CA2C/D"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { createElement } from "react";
|
|
2
|
+
import { definePlugin } from "@chances-ai/plugin-api";
|
|
3
|
+
import { SessionPicker } from "@chances-ai/tui";
|
|
4
|
+
/**
|
|
5
|
+
* `/resume [id]`
|
|
6
|
+
* - With an id: triggers a respawn that re-enters the chat loop with the
|
|
7
|
+
* specified session. Unknown id → error.
|
|
8
|
+
* - Without args in TUI: opens SessionPicker (last 10 by updatedAt).
|
|
9
|
+
* - Without args in text mode: lists session ids the user can pass.
|
|
10
|
+
*
|
|
11
|
+
* The actual session-swap is delegated to the chat loop via `deps.respawn`.
|
|
12
|
+
* The slash command itself doesn't mutate the live engine — that would leave
|
|
13
|
+
* the SessionManager reference dangling. Step 8 wires the respawn loop.
|
|
14
|
+
*/
|
|
15
|
+
export function makeResumePlugin(deps) {
|
|
16
|
+
const command = {
|
|
17
|
+
name: "resume",
|
|
18
|
+
description: "Switch to a previously-saved session.",
|
|
19
|
+
run: async (args, slashCtx) => {
|
|
20
|
+
const sessions = deps.store.list().slice(0, 10);
|
|
21
|
+
if (sessions.length === 0)
|
|
22
|
+
return "no saved sessions in this workspace yet";
|
|
23
|
+
if (args.length === 0) {
|
|
24
|
+
if (slashCtx.openModal) {
|
|
25
|
+
const picked = await slashCtx.openModal((resolve) => {
|
|
26
|
+
const items = sessions.map((s) => ({
|
|
27
|
+
id: s.id,
|
|
28
|
+
title: s.title,
|
|
29
|
+
updatedAt: s.updatedAt,
|
|
30
|
+
preview: previewOf(s.turns),
|
|
31
|
+
}));
|
|
32
|
+
return renderSessionPicker(items, resolve);
|
|
33
|
+
});
|
|
34
|
+
if (!picked)
|
|
35
|
+
return "resume cancelled";
|
|
36
|
+
deps.respawn(picked.id);
|
|
37
|
+
return `resuming ${picked.id} (${picked.title})`;
|
|
38
|
+
}
|
|
39
|
+
// Text mode listing.
|
|
40
|
+
return [
|
|
41
|
+
"Recent sessions (run /resume <id> to switch):",
|
|
42
|
+
...sessions.map((s) => ` ${s.id} ${s.updatedAt.slice(0, 10)} ${s.title}`),
|
|
43
|
+
].join("\n");
|
|
44
|
+
}
|
|
45
|
+
const target = args[0];
|
|
46
|
+
const found = sessions.find((s) => s.id === target);
|
|
47
|
+
if (!found)
|
|
48
|
+
return `unknown session id: ${target}. /resume shows recent ids.`;
|
|
49
|
+
deps.respawn(target);
|
|
50
|
+
return `resuming ${target} (${found.title})`;
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
return definePlugin({
|
|
54
|
+
name: "builtin/resume",
|
|
55
|
+
onLoad(ctx) {
|
|
56
|
+
ctx.registerSlashCommand(command);
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function previewOf(turns) {
|
|
61
|
+
for (const turn of turns) {
|
|
62
|
+
for (const msg of turn.messages) {
|
|
63
|
+
if (msg.role !== "user")
|
|
64
|
+
continue;
|
|
65
|
+
for (const part of msg.content) {
|
|
66
|
+
if (part.type === "text" && part.text.trim())
|
|
67
|
+
return part.text.trim();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
function renderSessionPicker(items, resolve) {
|
|
74
|
+
return createElement(SessionPicker, { sessions: items, onChoose: resolve });
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=resume.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resume.js","sourceRoot":"","sources":["../../src/slash/resume.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chances-ai/cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"chances": "./dist/entrypoints/cli-node.js",
|
|
@@ -12,18 +12,18 @@
|
|
|
12
12
|
"@ai-sdk/google": "^1.2.0",
|
|
13
13
|
"@ai-sdk/openai": "^1.3.0",
|
|
14
14
|
"@ai-sdk/openai-compatible": "^0.2.0",
|
|
15
|
-
"@chances-ai/ai": "
|
|
16
|
-
"@chances-ai/config": "
|
|
17
|
-
"@chances-ai/core": "
|
|
18
|
-
"@chances-ai/memory": "
|
|
19
|
-
"@chances-ai/native": "
|
|
20
|
-
"@chances-ai/plugin-api": "
|
|
21
|
-
"@chances-ai/plugin-logger": "
|
|
22
|
-
"@chances-ai/runtime": "
|
|
23
|
-
"@chances-ai/session": "
|
|
24
|
-
"@chances-ai/telemetry": "
|
|
25
|
-
"@chances-ai/tools": "
|
|
26
|
-
"@chances-ai/tui": "
|
|
15
|
+
"@chances-ai/ai": "3.0.0",
|
|
16
|
+
"@chances-ai/config": "3.0.0",
|
|
17
|
+
"@chances-ai/core": "3.0.0",
|
|
18
|
+
"@chances-ai/memory": "3.0.0",
|
|
19
|
+
"@chances-ai/native": "3.0.0",
|
|
20
|
+
"@chances-ai/plugin-api": "3.0.0",
|
|
21
|
+
"@chances-ai/plugin-logger": "3.0.0",
|
|
22
|
+
"@chances-ai/runtime": "3.0.0",
|
|
23
|
+
"@chances-ai/session": "3.0.0",
|
|
24
|
+
"@chances-ai/telemetry": "3.0.0",
|
|
25
|
+
"@chances-ai/tools": "3.0.0",
|
|
26
|
+
"@chances-ai/tui": "3.0.0",
|
|
27
27
|
"ai": "^4.3.0",
|
|
28
28
|
"ink": "^5.0.1",
|
|
29
29
|
"react": "^18.3.1",
|