papilot 0.1.0 → 0.1.1
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/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/bin/papilot.mjs +2 -2
- package/examples/papilot-paper/sections/02-using.tex +3 -3
- package/package.json +1 -1
- package/src/server/codex.ts +2 -2
- package/src/server/main.ts +2 -2
- package/src/server/paths.ts +2 -1
- package/src/server/workspace.ts +2 -1
package/README.md
CHANGED
|
@@ -75,7 +75,7 @@ npm install -g papilot
|
|
|
75
75
|
papilot demo
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
Open **http://127.0.0.1:
|
|
78
|
+
Open **http://127.0.0.1:7274** and choose **PDF** in the sidebar. The demo adds an example paper to revise: a 26-page LaTeX paper about Papilot itself, full of equations. When you are ready, run `papilot` and choose **Add paper** to add any LaTeX project in a Git repository.
|
|
79
79
|
|
|
80
80
|
## 🤖 Agents
|
|
81
81
|
|
package/README.zh-CN.md
CHANGED
|
@@ -75,7 +75,7 @@ npm install -g papilot
|
|
|
75
75
|
papilot demo
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
打开 **http://127.0.0.1:
|
|
78
|
+
打开 **http://127.0.0.1:7274**,在侧栏选择 **PDF**。demo 会添加一篇示例论文供你修改:一篇讲 Papilot 本身的 26 页 LaTeX 论文,满是公式。准备好之后,运行 `papilot`,选择 **Add paper**,即可添加任何放在 Git 仓库里的 LaTeX 项目。
|
|
79
79
|
|
|
80
80
|
## 🤖 智能体
|
|
81
81
|
|
package/bin/papilot.mjs
CHANGED
|
@@ -8,7 +8,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
8
8
|
|
|
9
9
|
const usage = `Usage: papilot [demo | codex <args> | service install | service stop] [--port <number>] [--data <folder>]
|
|
10
10
|
|
|
11
|
-
papilot Start Papilot at http://127.0.0.1:
|
|
11
|
+
papilot Start Papilot at http://127.0.0.1:7274
|
|
12
12
|
papilot demo Add the example paper, then start
|
|
13
13
|
papilot codex login Sign in to Codex with the Codex that Papilot uses
|
|
14
14
|
papilot service install Run Papilot in the background after login (macOS)
|
|
@@ -106,7 +106,7 @@ if (service) {
|
|
|
106
106
|
await import('../src/server/main.ts');
|
|
107
107
|
} catch (error) {
|
|
108
108
|
if (error?.code !== 'EADDRINUSE') throw error;
|
|
109
|
-
console.error(`Port ${process.env.PAPILOT_PORT ||
|
|
109
|
+
console.error(`Port ${process.env.PAPILOT_PORT || 7274} is already in use. Is Papilot already running? Start another copy with --port <number>.`);
|
|
110
110
|
process.exit(1);
|
|
111
111
|
}
|
|
112
112
|
if (!tex) console.warn('\nlatexmk was not found, so PDFs cannot be built yet. Install MacTeX or TeX Live (the full distribution), open a new terminal and run papilot again.');
|
|
@@ -13,7 +13,7 @@ installed and started with
|
|
|
13
13
|
npm install -g papilot
|
|
14
14
|
papilot
|
|
15
15
|
\end{verbatim}
|
|
16
|
-
and then opened at \texttt{http://127.0.0.1:
|
|
16
|
+
and then opened at \texttt{http://127.0.0.1:7274}. \emph{Add paper} registers a
|
|
17
17
|
\LaTeX{} Git repository on the host; later papers are added and switched from
|
|
18
18
|
the paper menu at the top of the sidebar. A repository must have at least one
|
|
19
19
|
commit and compile with \texttt{latexmk}; a custom root document is named in a
|
|
@@ -106,7 +106,7 @@ request (\cref{sec:security}):
|
|
|
106
106
|
export PAPILOT_PUBLIC_ORIGIN=https://your-mac.your-tailnet.ts.net:8443
|
|
107
107
|
export PAPILOT_TAILSCALE_LOGIN=your-tailscale-login
|
|
108
108
|
papilot service install
|
|
109
|
-
tailscale serve --bg --https=8443 http://127.0.0.1:
|
|
109
|
+
tailscale serve --bg --https=8443 http://127.0.0.1:7274
|
|
110
110
|
\end{verbatim}
|
|
111
111
|
A background service starts Papilot after login, restarts it if it exits and
|
|
112
112
|
keeps the computer from sleeping while it runs. Without Tailscale, an SSH
|
|
@@ -120,7 +120,7 @@ tunnel serves the same purpose.
|
|
|
120
120
|
\toprule
|
|
121
121
|
Variable & Purpose \\
|
|
122
122
|
\midrule
|
|
123
|
-
\texttt{PAPILOT\_PORT} & local HTTP port, $
|
|
123
|
+
\texttt{PAPILOT\_PORT} & local HTTP port, $7274$ by default \\
|
|
124
124
|
\texttt{PAPILOT\_DATA} & state and generated example, \texttt{\textasciitilde/.papilot} by default \\
|
|
125
125
|
\texttt{PAPILOT\_CODEX\_BIN} & Codex executable \\
|
|
126
126
|
\texttt{ANTHROPIC\_API\_KEY} & API key for Claude tasks \\
|
package/package.json
CHANGED
package/src/server/codex.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { spawn, type ChildProcessWithoutNullStreams } from 'node:child_process';
|
|
2
2
|
import { createInterface } from 'node:readline';
|
|
3
3
|
import { EventEmitter } from 'node:events';
|
|
4
|
-
import { CODEX, ROOT } from './paths.ts';
|
|
4
|
+
import { CODEX, ROOT, VERSION } from './paths.ts';
|
|
5
5
|
|
|
6
6
|
export class RpcResponseError extends Error {}
|
|
7
7
|
|
|
@@ -34,7 +34,7 @@ export class Codex extends EventEmitter {
|
|
|
34
34
|
};
|
|
35
35
|
child.once('error', failed);
|
|
36
36
|
child.once('exit', (code, signal) => failed(new Error(`Codex exited (${signal || code})`)));
|
|
37
|
-
await this.request('initialize', { clientInfo: { name: 'papilot', title: 'Papilot', version:
|
|
37
|
+
await this.request('initialize', { clientInfo: { name: 'papilot', title: 'Papilot', version: VERSION } });
|
|
38
38
|
this.notify('initialized', {}); this.ready = true;
|
|
39
39
|
}
|
|
40
40
|
private receive(message: any) {
|
package/src/server/main.ts
CHANGED
|
@@ -6,7 +6,7 @@ import path from 'node:path';
|
|
|
6
6
|
import { randomBytes, randomUUID, timingSafeEqual } from 'node:crypto';
|
|
7
7
|
import { Store } from './store.ts';
|
|
8
8
|
import { Workspace } from './workspace.ts';
|
|
9
|
-
import { ROOT, DATA, PORT, PUBLIC_ORIGIN, OWNER } from './paths.ts';
|
|
9
|
+
import { ROOT, DATA, PORT, PUBLIC_ORIGIN, OWNER, VERSION } from './paths.ts';
|
|
10
10
|
import { reviewClient, allowedReviewRoute, rewriteReviewHtml, rewriteReviewJson, confirmedReviewResponse, annotateReviewState } from './review.ts';
|
|
11
11
|
import { authorize } from './security.ts';
|
|
12
12
|
import { fullPaperPdf } from './pdf.ts';
|
|
@@ -27,7 +27,7 @@ app.addHook('onRequest', async (request, reply) => {
|
|
|
27
27
|
if (request.method === 'POST' && !request.url.startsWith('/review/') && !equals(String(request.headers['x-papilot-token'] || ''), csrf)) return reply.code(403).send({ error: 'This page has expired. Refresh it and try again.' });
|
|
28
28
|
});
|
|
29
29
|
app.setErrorHandler((error, _request, reply) => { reply.code((error as any).statusCode || 400).send({ error: (error as Error).message }); });
|
|
30
|
-
app.get('/api/health', () => ({ ok: true, name: 'Papilot', version:
|
|
30
|
+
app.get('/api/health', () => ({ ok: true, name: 'Papilot', version: VERSION }));
|
|
31
31
|
app.get('/api/state', () => ({ ...workspace.snapshot(), csrf }));
|
|
32
32
|
app.get('/api/events', (request, reply) => {
|
|
33
33
|
reply.hijack();
|
package/src/server/paths.ts
CHANGED
|
@@ -4,7 +4,8 @@ import { fileURLToPath } from 'node:url';
|
|
|
4
4
|
export const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
|
|
5
5
|
export const DATA = path.resolve(process.env.PAPILOT_DATA || path.join(ROOT, '.papilot'));
|
|
6
6
|
export const ENGINE = path.resolve(process.env.PAPILOT_REVIEW_ENGINE || path.join(ROOT, 'vendor/latex-review'));
|
|
7
|
-
export const
|
|
7
|
+
export const VERSION: string = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf8')).version;
|
|
8
|
+
export const PORT = Number(process.env.PAPILOT_PORT || 7274);
|
|
8
9
|
export const PUBLIC_ORIGIN = process.env.PAPILOT_PUBLIC_ORIGIN || '';
|
|
9
10
|
export const OWNER = process.env.PAPILOT_TAILSCALE_LOGIN || '';
|
|
10
11
|
export const CODEX = process.env.PAPILOT_CODEX_BIN || (fs.existsSync(path.join(ROOT, 'node_modules/.bin/codex')) ? path.join(ROOT, 'node_modules/.bin/codex') : 'codex');
|
package/src/server/workspace.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { Store } from './store.ts';
|
|
|
8
8
|
import { Codex, RpcResponseError } from './codex.ts';
|
|
9
9
|
import { AGENT_NAMES, AntigravityAgent, ClaudeAgent, type AgentRuntime } from './agents.ts';
|
|
10
10
|
import { reviewClient, taskRoute, confirmedReviewResponse, type ReviewClient } from './review.ts';
|
|
11
|
+
import { VERSION } from './paths.ts';
|
|
11
12
|
import type { Task, Run, TimelineItem, PendingRequest, RuntimeStatus, Snapshot, PendingReview, AgentModel, Usage, AgentId } from '../shared.ts';
|
|
12
13
|
|
|
13
14
|
const execute = promisify(execFile);
|
|
@@ -83,7 +84,7 @@ export class Workspace extends EventEmitter {
|
|
|
83
84
|
if (id === 'codex') this.loaded.clear();
|
|
84
85
|
this.emit('change');
|
|
85
86
|
}
|
|
86
|
-
snapshot(): Snapshot { return { version:
|
|
87
|
+
snapshot(): Snapshot { return { version: VERSION, projects: this.store.projects(), tasks: this.store.tasks(), requests: [...this.requests.values()].map(({ rpcId: _, agent: __, ...r }) => r), runtime: this.runtime, runtimes: this.runtimes }; }
|
|
87
88
|
async connect() {
|
|
88
89
|
if (this.connecting) return this.connecting;
|
|
89
90
|
this.connecting = Promise.all(AGENTS.map(id => this.connectAgent(id))).then(() => { this.emit('change'); this.schedule(); }).finally(() => { this.connecting = null; });
|