u1s1-cli 0.10.0 → 0.11.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.
@@ -0,0 +1,32 @@
1
+ import { type CliConfig } from "./config.js";
2
+ /** pi auto-appends <agentDir>/APPEND_SYSTEM.md to its system prompt — our branding hook. */
3
+ export declare function ensureBrandPrompt(): void;
4
+ /** Defaults that don't overwrite values the user already set. */
5
+ export declare function ensureDefaultSettings(): void;
6
+ /** ≤0.4.0 wrote u1s1-dark/u1s1-light into the pi themes dir; remove them. */
7
+ export declare function cleanupBrandThemes(): void;
8
+ /**
9
+ * u1s1 provider for SDK-based frontends (`u1s1 web`): pi composes
10
+ * <agentDir>/models.json above registered providers, so this exposes the same
11
+ * models as the TUI's in-process extension without patching the frontend.
12
+ * apiKey stays an env reference — the launcher sets U1S1_API_KEY, the file
13
+ * itself holds no secret. Rewritten on every launch so baseUrl/model changes
14
+ * propagate; other providers a user may have added are preserved.
15
+ */
16
+ /**
17
+ * 联网工具经 <agentDir>/extensions 投影:pi 的资源加载器对 TUI 和 `u1s1 web`
18
+ * 都会扫描这个目录,两个前端从此共用同一份工具注册,不用各接一遍。
19
+ * 文件每次启动重写(baseUrl/服务端开关变化随之生效);apiKey 走 U1S1_API_KEY
20
+ * 环境变量,文件里不落密钥。U1S1_TOOLS_VIA_EXTENSION 守卫:旧版 CLI 仍在
21
+ * 进程内注册工具且不设该变量,残留的本文件在旧版下自动空转,避免双重注册。
22
+ */
23
+ export declare function writeWebToolsExtension(cfg: CliConfig, webSearchEnabled: boolean): void;
24
+ /**
25
+ * pi-web-ui 的就绪检测只认 <agentDir>/auth.json 里有没有凭据条目(models.json
26
+ * 的 apiKey 它不看),空 {} 会在网页里弹「未检测到 pi agent 配置」引导装 pi。
27
+ * 这里补一条 u1s1 的 api_key 记录 —— key 用 $U1S1_API_KEY 环境变量引用,
28
+ * pi 的 resolveConfigValue 读取时才解析,文件本身不落密钥,和 models.json 同款。
29
+ * 其他 provider 的凭据原样保留;文件损坏时不动它,交给 SDK 自己报错。
30
+ */
31
+ export declare function ensureAuthCredential(): void;
32
+ export declare function ensureProviderModels(cfg: CliConfig): void;
@@ -106,6 +106,33 @@ export function writeWebToolsExtension(cfg, webSearchEnabled) {
106
106
  ` pi.registerTool(tools.webFetchTool);\n` +
107
107
  `}\n`);
108
108
  }
109
+ /**
110
+ * pi-web-ui 的就绪检测只认 <agentDir>/auth.json 里有没有凭据条目(models.json
111
+ * 的 apiKey 它不看),空 {} 会在网页里弹「未检测到 pi agent 配置」引导装 pi。
112
+ * 这里补一条 u1s1 的 api_key 记录 —— key 用 $U1S1_API_KEY 环境变量引用,
113
+ * pi 的 resolveConfigValue 读取时才解析,文件本身不落密钥,和 models.json 同款。
114
+ * 其他 provider 的凭据原样保留;文件损坏时不动它,交给 SDK 自己报错。
115
+ */
116
+ export function ensureAuthCredential() {
117
+ mkdirSync(agentDir, { recursive: true });
118
+ const p = join(agentDir, "auth.json");
119
+ let root = {};
120
+ if (existsSync(p)) {
121
+ try {
122
+ root = JSON.parse(readFileSync(p, "utf8"));
123
+ }
124
+ catch {
125
+ return;
126
+ }
127
+ if (typeof root !== "object" || root === null || Array.isArray(root))
128
+ return;
129
+ }
130
+ const entry = root[PROVIDER_ID];
131
+ if (entry?.["type"] === "api_key" && entry["key"] === "$U1S1_API_KEY")
132
+ return;
133
+ root[PROVIDER_ID] = { type: "api_key", key: "$U1S1_API_KEY" };
134
+ writeFileSync(p, JSON.stringify(root, null, 2) + "\n", { mode: 0o600 });
135
+ }
109
136
  export function ensureProviderModels(cfg) {
110
137
  mkdirSync(agentDir, { recursive: true });
111
138
  const p = join(agentDir, "models.json");
package/dist/api.d.ts ADDED
@@ -0,0 +1,51 @@
1
+ import { type CliConfig } from "./config.js";
2
+ export interface MeResponse {
3
+ email: string | null;
4
+ signup_credit_usd?: number;
5
+ monthly_free_usd?: number;
6
+ daily_free_usd: number;
7
+ daily_free_used_usd: number;
8
+ daily_free_remaining_usd: number;
9
+ daily_free_resets_at: string;
10
+ daily_free_model: string;
11
+ mtd_usd: number;
12
+ balance_spent_usd: number;
13
+ bonus_balance_usd: number;
14
+ remaining_usd: number;
15
+ /** USD→默认模型 Token 折算率;老网关没有该字段,展示时需兜底回 $ */
16
+ tokens_per_usd?: number;
17
+ }
18
+ export interface ApiModel {
19
+ id: string;
20
+ name: string;
21
+ reasoning: boolean;
22
+ context_length: number;
23
+ max_tokens: number;
24
+ price: {
25
+ input: number;
26
+ output: number;
27
+ cache_read: number | null;
28
+ };
29
+ }
30
+ /** 服务端能力开关;字段缺失(老网关)时按开启处理,保持现状。 */
31
+ export interface ApiFeatures {
32
+ web_search?: boolean;
33
+ }
34
+ export interface ModelsResponse {
35
+ models: ApiModel[];
36
+ features: ApiFeatures;
37
+ }
38
+ export declare function fetchModels(cfg: CliConfig): Promise<ModelsResponse>;
39
+ export interface SearchResult {
40
+ title: string;
41
+ url: string;
42
+ snippet: string;
43
+ }
44
+ export interface SearchResponse {
45
+ query: string;
46
+ answer: string | null;
47
+ results: SearchResult[];
48
+ }
49
+ /** 联网搜索走网关代理(上游 key 只在服务端)。maxResults 不传时由服务端决定默认值。 */
50
+ export declare function searchWeb(cfg: Pick<CliConfig, "baseUrl" | "apiKey">, query: string, maxResults?: number, signal?: AbortSignal): Promise<SearchResponse>;
51
+ export declare function fetchMe(cfg: CliConfig): Promise<MeResponse>;
@@ -0,0 +1,13 @@
1
+ import type { Theme } from "@earendil-works/pi-coding-agent";
2
+ export declare const BRAND_NAME = "u1s1";
3
+ export declare const BRAND_CN = "\u6709\u4E00\u8BF4\u4E00";
4
+ export declare const BRAND_TAGLINE = "\u8BF4\u4EBA\u8BDD\u7684 AI \u7F16\u7A0B\u642D\u5B50";
5
+ export declare const DASHBOARD_URL = "https://u1s1.io/dashboard";
6
+ export declare function formatHomePath(path: string): string;
7
+ export declare const HERO_ART: string[];
8
+ /**
9
+ * Startup hero, responsive to terminal width:
10
+ * wide → wordmark with info column beside it; medium → stacked; narrow → one-liner.
11
+ */
12
+ export declare function renderBrandHeader(theme: Theme, version: string, cwd: string, width: number, notice?: string): string[];
13
+ export declare function printConsoleBanner(version: string): void;
@@ -0,0 +1,77 @@
1
+ export declare const VERSION: string;
2
+ /**
3
+ * 便携包安装(install.sh / install.ps1):包根旁边带自己的 node 运行时,
4
+ * npm 更新碰不到这份拷贝,升级只能整包重装。npm 全局安装的包在 node_modules
5
+ * 下,按父目录名先排除,避免撞上恰好叫 node 的目录误判。
6
+ */
7
+ export declare function isPortableInstall(): boolean;
8
+ export declare const DEFAULT_BASE_URL = "https://api.u1s1.io/v1";
9
+ export declare const PROVIDER_ID = "u1s1";
10
+ /**
11
+ * Convert an API model response to our internal ModelDef.
12
+ * Aliases are derived from the short id; note is left empty (filled by /model command).
13
+ */
14
+ export declare function apiModelToDef(m: {
15
+ id: string;
16
+ name: string;
17
+ reasoning: boolean;
18
+ context_length: number;
19
+ max_tokens: number;
20
+ price: {
21
+ input: number;
22
+ output: number;
23
+ cache_read: number | null;
24
+ };
25
+ }): ModelDef;
26
+ export interface ModelDef {
27
+ id: string;
28
+ name: string;
29
+ /** short aliases accepted by `u1s1 model <name>` */
30
+ aliases: string[];
31
+ reasoning: boolean;
32
+ contextWindow: number;
33
+ maxTokens: number;
34
+ /** USD per million tokens — display/estimation only; billing is server-side */
35
+ cost: {
36
+ input: number;
37
+ output: number;
38
+ cacheRead: number;
39
+ cacheWrite: number;
40
+ };
41
+ note: string;
42
+ }
43
+ export declare const MODELS: ModelDef[];
44
+ /** Replace MODELS with a fresh list fetched from server (e.g. at startup). */
45
+ export declare function setModelsFromApi(apiModels: ModelDef[]): void;
46
+ export declare const DEFAULT_MODEL_ID: string;
47
+ export declare function resolveModel(nameOrAlias: string): ModelDef | undefined;
48
+ export interface CliConfig {
49
+ apiKey?: string;
50
+ baseUrl: string;
51
+ /** preferred model id; must be one of MODELS */
52
+ model?: string;
53
+ }
54
+ export declare const u1s1Dir: string;
55
+ /** pi keeps auth/models/settings/sessions under this dir — isolated from any real pi install. */
56
+ export declare const agentDir: string;
57
+ export declare const agentSettingsFile: string;
58
+ export interface AgentSettings {
59
+ showStartupBanner?: boolean;
60
+ autoUpdate?: boolean;
61
+ [key: string]: unknown;
62
+ }
63
+ /** Read u1s1 agent settings (settings.json). Returns empty object if missing or invalid. */
64
+ export declare function readSettings(): AgentSettings;
65
+ /** Model last chosen in-session via /model (pi writes this). */
66
+ export declare function readAgentDefaultModel(): string | undefined;
67
+ /** Keep pi's settings.json in sync so /model and `u1s1 model` share one default. */
68
+ export declare function writeAgentDefaultModel(modelId: string): void;
69
+ /**
70
+ * After this fix both stores stay in sync. If they still disagree (old installs),
71
+ * prefer the in-session /model value — that's the one users thought they had set.
72
+ */
73
+ export declare function resolvePreferredModel(configModel: string | undefined): string;
74
+ export declare function loadConfig(): CliConfig;
75
+ export declare function saveConfig(cfg: CliConfig): void;
76
+ /** Persist the user's preferred model to both stores. */
77
+ export declare function persistPreferredModel(cfg: CliConfig, modelId: string): CliConfig;
@@ -0,0 +1,2 @@
1
+ import { type CliConfig } from "./config.js";
2
+ export declare function deployCommand(cfg: CliConfig, args: string[]): Promise<void>;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * 桌面版 App(packages/app)的嵌入 API,对外路径 "u1s1-cli/embed"(见
3
+ * package.json exports)。App 与 CLI 共享 ~/.u1s1 的登录态和 agentDir 配置,
4
+ * 这里只做转发,不放实现 —— 保证两个入口永远走同一份逻辑。
5
+ */
6
+ export { agentDir, loadConfig, saveConfig, u1s1Dir, VERSION, type CliConfig, } from "./config.js";
7
+ export { fetchMe, fetchModels } from "./api.js";
8
+ export { apiOrigin, pollDeviceLogin, startDeviceLogin, type DeviceStart } from "./login.js";
9
+ export { prepareWebEnv } from "./web.js";
10
+ export { applyWebUiBranding, applyWebUiFrontend } from "./webui-brand.js";
11
+ export { DASHBOARD_URL } from "./brand.js";
package/dist/embed.js ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * 桌面版 App(packages/app)的嵌入 API,对外路径 "u1s1-cli/embed"(见
3
+ * package.json exports)。App 与 CLI 共享 ~/.u1s1 的登录态和 agentDir 配置,
4
+ * 这里只做转发,不放实现 —— 保证两个入口永远走同一份逻辑。
5
+ */
6
+ export { agentDir, loadConfig, saveConfig, u1s1Dir, VERSION, } from "./config.js";
7
+ export { fetchMe, fetchModels } from "./api.js";
8
+ export { apiOrigin, pollDeviceLogin, startDeviceLogin } from "./login.js";
9
+ export { prepareWebEnv } from "./web.js";
10
+ export { applyWebUiBranding, applyWebUiFrontend } from "./webui-brand.js";
11
+ export { DASHBOARD_URL } from "./brand.js";
@@ -0,0 +1,3 @@
1
+ import type { SourceAdapter, SourceSession } from "./types.js";
2
+ export declare function hydrateClaudeSession(session: SourceSession): Promise<SourceSession>;
3
+ export declare const claudeAdapter: SourceAdapter;
@@ -0,0 +1,3 @@
1
+ import type { SourceAdapter, SourceSession } from "./types.js";
2
+ export declare function hydrateCodexSession(session: SourceSession): Promise<SourceSession>;
3
+ export declare const codexAdapter: SourceAdapter;
@@ -0,0 +1 @@
1
+ export declare function importCommand(args: string[]): Promise<void>;
@@ -0,0 +1,104 @@
1
+ export type ImportSourceId = "claude" | "codex";
2
+ export interface SourceSession {
3
+ source: ImportSourceId;
4
+ /** Stable id from the original tool (session uuid). */
5
+ sourceId: string;
6
+ sourcePath: string;
7
+ cwd: string;
8
+ title?: string;
9
+ startedAt?: number;
10
+ mtimeMs: number;
11
+ }
12
+ export interface DiscoverOpts {
13
+ /** If set, only sessions that belong to this working directory. */
14
+ cwd?: string;
15
+ }
16
+ export type ImportedContent = {
17
+ type: "text";
18
+ text: string;
19
+ } | {
20
+ type: "thinking";
21
+ thinking: string;
22
+ } | {
23
+ type: "toolCall";
24
+ id: string;
25
+ name: string;
26
+ arguments: Record<string, unknown>;
27
+ };
28
+ export interface ImportedUser {
29
+ role: "user";
30
+ text: string;
31
+ timestamp: number;
32
+ }
33
+ export interface ImportedAssistant {
34
+ role: "assistant";
35
+ content: ImportedContent[];
36
+ provider: string;
37
+ model: string;
38
+ api: string;
39
+ stopReason: "stop" | "length" | "toolUse" | "error" | "aborted";
40
+ usage?: {
41
+ input: number;
42
+ output: number;
43
+ cacheRead: number;
44
+ cacheWrite: number;
45
+ };
46
+ timestamp: number;
47
+ }
48
+ export interface ImportedToolResult {
49
+ role: "toolResult";
50
+ toolCallId: string;
51
+ toolName: string;
52
+ text: string;
53
+ isError: boolean;
54
+ timestamp: number;
55
+ }
56
+ export type ImportedMessage = ImportedUser | ImportedAssistant | ImportedToolResult;
57
+ export interface ConvertedSession {
58
+ cwd: string;
59
+ title?: string;
60
+ messages: ImportedMessage[];
61
+ }
62
+ export interface SourceAdapter {
63
+ id: ImportSourceId;
64
+ label: string;
65
+ discover(opts: DiscoverOpts): SourceSession[];
66
+ convert(session: SourceSession): ConvertedSession;
67
+ }
68
+ export interface ImportRecord {
69
+ source: ImportSourceId;
70
+ sourceId: string;
71
+ sourcePath: string;
72
+ destPath: string;
73
+ cwd: string;
74
+ title?: string;
75
+ importedAt: string;
76
+ }
77
+ export interface ImportIndex {
78
+ version: 1;
79
+ items: Record<string, ImportRecord>;
80
+ }
81
+ export interface ImportOptions {
82
+ sources: ImportSourceId[];
83
+ /** Only this project. Omit to import every discovered session. */
84
+ cwd?: string;
85
+ dryRun: boolean;
86
+ force: boolean;
87
+ limit?: number;
88
+ }
89
+ export interface ImportResultItem {
90
+ source: ImportSourceId;
91
+ sourceId: string;
92
+ title: string;
93
+ cwd: string;
94
+ destPath?: string;
95
+ status: "imported" | "skipped" | "empty" | "error";
96
+ detail?: string;
97
+ }
98
+ export interface ImportSummary {
99
+ items: ImportResultItem[];
100
+ imported: number;
101
+ skipped: number;
102
+ empty: number;
103
+ errors: number;
104
+ }
@@ -0,0 +1,26 @@
1
+ export declare const MAX_TOOL_RESULT_CHARS = 80000;
2
+ export declare const MAX_TEXT_CHARS = 200000;
3
+ export declare const PREVIEW_TITLE_CHARS = 48;
4
+ export declare function resolveExistingDir(path: string): string | undefined;
5
+ export declare function listHomeClaudeDirs(): string[];
6
+ export declare function uniqueExistingDirs(paths: Array<string | undefined>): string[];
7
+ export declare function encodeClaudeProjectDir(cwd: string): string;
8
+ export declare function samePath(a: string, b: string): boolean;
9
+ /** Nearest git root at or above cwd. Stops at home so `~/` is never treated as a mega-project. */
10
+ export declare function projectRoot(cwd: string): string | undefined;
11
+ /** cwd + ancestors up to the git root (or just cwd when there is no repo). */
12
+ export declare function projectAncestors(cwd: string): string[];
13
+ /** Same folder, anywhere inside this git repo, or the repo root when you're in a subfolder. */
14
+ export declare function sessionBelongsToCwd(sessionCwd: string, wanted: string): boolean;
15
+ export declare function parseJsonLine(line: string): Record<string, unknown> | undefined;
16
+ export declare function asRecord(value: unknown): Record<string, unknown> | undefined;
17
+ export declare function asString(value: unknown): string | undefined;
18
+ export declare function parseTime(value: unknown): number | undefined;
19
+ export declare function truncateText(text: string, max: number): string;
20
+ export declare function formatBytes(n: number): string;
21
+ export declare function oneLine(text: string, max?: number): string;
22
+ export declare function firstMeaningfulLine(text: string): string;
23
+ export declare function parseArgsJson(raw: string): Record<string, unknown>;
24
+ export declare function fileMtimeMs(path: string): number;
25
+ export declare function isProbablyInjection(text: string): boolean;
26
+ export declare function readFirstJsonObject(path: string): Record<string, unknown> | undefined;
@@ -0,0 +1,4 @@
1
+ import type { ConvertedSession } from "./types.js";
2
+ export declare function writeConvertedSession(converted: ConvertedSession, destCwd?: string): string;
3
+ export declare function readJsonIfExists(path: string): unknown;
4
+ export declare function writeJson(path: string, value: unknown): void;
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ import { cleanupBrandThemes, ensureBrandPrompt, ensureDefaultSettings, ensurePro
5
5
  import { printConsoleBanner } from "./brand.js";
6
6
  import { agentDir, apiModelToDef, isPortableInstall, loadConfig, MODELS, persistPreferredModel, PROVIDER_ID, readSettings, resolvePreferredModel, setModelsFromApi, VERSION, } from "./config.js";
7
7
  import { applyBrandUi, setUpdateNotice } from "./style.js";
8
+ import { offerStarterTemplates } from "./templates.js";
8
9
  import { fetchModels } from "./api.js";
9
10
  const PACKAGE_NAME = "u1s1-cli";
10
11
  /**
@@ -167,6 +168,7 @@ async function runAgent(cfg, args) {
167
168
  name: "u1s1",
168
169
  factory: (pi) => {
169
170
  applyBrandUi(pi, VERSION);
171
+ offerStarterTemplates(pi);
170
172
  // pi 内置只有 /quit;补一个更常见的 /exit,方便新手退出
171
173
  pi.registerCommand("exit", {
172
174
  description: "退出 u1s1",
@@ -0,0 +1,16 @@
1
+ import { type CliConfig } from "./config.js";
2
+ /** baseUrl 形如 https://api.u1s1.io/v1;auth 路由挂在同一域名的根路径。 */
3
+ export declare function apiOrigin(cfg: CliConfig): string;
4
+ export interface DeviceStart {
5
+ verify_url: string;
6
+ poll_secret: string;
7
+ interval: number;
8
+ expires_in: number;
9
+ }
10
+ /** 发起浏览器登录;网关太老或连不上时返回 null,退回手动粘贴。 */
11
+ export declare function startDeviceLogin(origin: string): Promise<DeviceStart | null>;
12
+ /** 轮询等浏览器那边批准;拿到 key 返回,过期返回 null。 */
13
+ export declare function pollDeviceLogin(origin: string, start: DeviceStart): Promise<string | null>;
14
+ export declare function login(keyArg?: string): Promise<CliConfig>;
15
+ /** Returns a config that definitely has an apiKey, prompting the user if needed. */
16
+ export declare function ensureAuth(): Promise<CliConfig>;
package/dist/login.js CHANGED
@@ -18,11 +18,11 @@ function tryOpenBrowser(url) {
18
18
  }
19
19
  const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
20
20
  /** baseUrl 形如 https://api.u1s1.io/v1;auth 路由挂在同一域名的根路径。 */
21
- function apiOrigin(cfg) {
21
+ export function apiOrigin(cfg) {
22
22
  return cfg.baseUrl.replace(/\/v1\/?$/, "");
23
23
  }
24
24
  /** 发起浏览器登录;网关太老或连不上时返回 null,退回手动粘贴。 */
25
- async function startDeviceLogin(origin) {
25
+ export async function startDeviceLogin(origin) {
26
26
  try {
27
27
  const resp = await fetch(`${origin}/auth/device/start`, { method: "POST" });
28
28
  if (!resp.ok)
@@ -42,7 +42,7 @@ async function startDeviceLogin(origin) {
42
42
  }
43
43
  }
44
44
  /** 轮询等浏览器那边批准;拿到 key 返回,过期返回 null。 */
45
- async function pollDeviceLogin(origin, start) {
45
+ export async function pollDeviceLogin(origin, start) {
46
46
  const deadline = Date.now() + start.expires_in * 1000;
47
47
  while (Date.now() < deadline) {
48
48
  await sleep(start.interval * 1000);
@@ -0,0 +1 @@
1
+ export declare function modelCommand(nameOrAlias?: string): Promise<void>;
@@ -0,0 +1 @@
1
+ export declare function createWebShortcut(): void;
@@ -0,0 +1,7 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ export declare function setUpdateNotice(notice: string): void;
3
+ /**
4
+ * Brand chrome is just the startup hero + window title; everything else
5
+ * (tool rows, thinking blocks, spinner) stays Pi's default UI.
6
+ */
7
+ export declare function applyBrandUi(pi: ExtensionAPI, version: string): void;
@@ -0,0 +1,2 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ export declare function offerStarterTemplates(pi: ExtensionAPI): void;
@@ -0,0 +1,48 @@
1
+ import { readdirSync } from "node:fs";
2
+ /**
3
+ * 空目录首跑模板(roadmap-next #3):小白第一次在空文件夹里启动 u1s1,
4
+ * 面对空白输入框往往想不出第一句说什么。弹一个选择器给几个现成的小项目,
5
+ * 选中后把完整提示词放进输入框——不直接替用户发送,让他看到「原来就是
6
+ * 说这样一句话」,想改就改,回车才开跑。选「我自己说」或按 Esc 都不打扰。
7
+ */
8
+ const TEMPLATES = [
9
+ {
10
+ label: "🏠 个人主页——介绍我自己的网页",
11
+ prompt: "帮我做一个个人主页网页,介绍我自己,放上我的爱好和联系方式,风格简洁好看,手机和电脑打开都正常。做完教我怎么在浏览器里预览。",
12
+ },
13
+ {
14
+ label: "📅 倒数日——距离某天还有多少天",
15
+ prompt: "做一个倒数日网页,可以输入日期和事件名,大字显示距离那天还有多少天,设计得可爱一点。做完教我怎么在浏览器里预览。",
16
+ },
17
+ {
18
+ label: "🖼️ 电子相册——可以翻页的照片墙",
19
+ prompt: "帮我做一个电子相册网页,照片可以左右翻页,先放几张占位图,并告诉我之后怎么换成自己的照片。做完教我怎么在浏览器里预览。",
20
+ },
21
+ ];
22
+ const SELF_DESCRIBE = "✍️ 不用了,我自己说";
23
+ /** 只看可见条目:.git / .DS_Store 这类隐藏文件不算「有东西」。读不了就当非空,不打扰。 */
24
+ function isEmptyDir(dir) {
25
+ try {
26
+ return readdirSync(dir).filter((name) => !name.startsWith(".")).length === 0;
27
+ }
28
+ catch {
29
+ return false;
30
+ }
31
+ }
32
+ let offered = false;
33
+ export function offerStarterTemplates(pi) {
34
+ pi.on("session_start", async (_event, ctx) => {
35
+ // /clear 等新会话也会触发 session_start,每次启动只弹一次
36
+ if (ctx.mode !== "tui" || offered)
37
+ return;
38
+ if (!isEmptyDir(process.cwd()))
39
+ return;
40
+ offered = true;
41
+ const choice = await ctx.ui.select("这个文件夹还是空的,挑一个现成的小项目直接开跑?", [...TEMPLATES.map((t) => t.label), SELF_DESCRIBE]);
42
+ const t = TEMPLATES.find((x) => x.label === choice);
43
+ if (!t)
44
+ return;
45
+ ctx.ui.setEditorText(t.prompt);
46
+ ctx.ui.notify("提示词放进输入框了:想改哪句直接改,按回车开跑", "info");
47
+ });
48
+ }
@@ -0,0 +1,18 @@
1
+ import { Type } from "typebox";
2
+ import type { CliConfig } from "./config.js";
3
+ /** 联网搜索工具:走 u1s1 网关代理,上游 key 不落到用户机器上。 */
4
+ export declare function createSearchTool(cfg: Pick<CliConfig, "baseUrl" | "apiKey">): import("@earendil-works/pi-coding-agent").ToolDefinition<Type.TObject<{
5
+ query: Type.TString;
6
+ maxResults: Type.TOptional<Type.TNumber>;
7
+ }>, {
8
+ query: string;
9
+ count: number;
10
+ }, any> & import("@earendil-works/pi-coding-agent").ToolDefinition<any, any, any>;
11
+ /** 抓网页:纯客户端出网,不经过我们的服务器,也不额外计费。 */
12
+ export declare const webFetchTool: import("@earendil-works/pi-coding-agent").ToolDefinition<Type.TObject<{
13
+ url: Type.TString;
14
+ }>, {
15
+ url: string;
16
+ contentType: string;
17
+ chars: number;
18
+ }, any> & import("@earendil-works/pi-coding-agent").ToolDefinition<any, any, any>;
@@ -0,0 +1,8 @@
1
+ export declare const VERSION: string;
2
+ export declare const PACKAGE_NAME = "u1s1-cli";
3
+ /** Detect the package manager that installed u1s1. */
4
+ export declare function detectPackageManager(): string;
5
+ /** Fetch the latest published version from npm registry. */
6
+ export declare function getLatestVersion(): Promise<string | undefined>;
7
+ export declare function compareVersions(a: string, b: string): number;
8
+ export declare function update(): Promise<void>;
@@ -0,0 +1 @@
1
+ export declare function usage(): Promise<void>;
package/dist/web.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ import { type CliConfig } from "./config.js";
2
+ /**
3
+ * `u1s1 web` — 浏览器网页版。薄包装 pi-web-ui 的服务器:注入我们的 agentDir
4
+ * (品牌 prompt / models.json / 会话与 TUI 共享)和登录 key,其余原样透传
5
+ * (--port / --cwd / --no-browser)。
6
+ */
7
+ /**
8
+ * 启动 pi-web-ui 服务器前的公共准备:品牌 prompt、模型列表、auth.json 凭据、
9
+ * 联网工具扩展、默认模型,并返回子进程需要的环境变量。CLI(`u1s1 web`)和
10
+ * 桌面版 App(packages/app,经 "u1s1-cli/embed" 引入)共用这一份逻辑。
11
+ */
12
+ export declare function prepareWebEnv(cfg: CliConfig): Promise<Record<string, string>>;
13
+ export declare function webCommand(cfg: CliConfig, args: string[]): Promise<void>;