input-kanban 0.0.10 → 0.0.12
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/ENVIRONMENT.md +1 -0
- package/README.en.md +2 -0
- package/README.md +2 -0
- package/RELEASE_NOTES.md +28 -0
- package/package.json +1 -1
- package/public/index.html +31 -8
- package/src/utils.js +2 -1
package/ENVIRONMENT.md
CHANGED
|
@@ -43,6 +43,7 @@ input-kanban \
|
|
|
43
43
|
- `input-kanban serve` starts a lightweight background scheduler that uses the same orchestrator auto-advance path as CLI `submit --auto` / `input-kanban auto <runId>`. It advances planned runs, serial batches, final judge startup, and bounded automatic retries without relying on an open browser tab.
|
|
44
44
|
- `KANBAN_RUNNER` / `--runner tmux` runs Codex tasks inside tmux windows while keeping scheduling and status tracking in the Node.js orchestrator.
|
|
45
45
|
- `KANBAN_RUNNER=tmux` is optional. Use it when you want live terminal visibility into planner, worker, and final judge sessions.
|
|
46
|
+
- With `KANBAN_RUNNER=tmux`, stopping and restarting `input-kanban serve` does not interrupt already-running Codex sessions; tmux keeps them alive and the scheduler resumes after restart. Do not assume the same safety for `headless` runner child processes.
|
|
46
47
|
- tmux mode uses one session per run and one window for planner, each batch, and judge. Batch windows contain an overview pane plus worker panes.
|
|
47
48
|
- tmux role windows stay open after the Codex command exits. The runner writes `exit_code` before entering the keep-open shell so Node.js status refresh can continue to advance from filesystem state.
|
|
48
49
|
- The dashboard exposes the run-level `tmux attach-session` copy action after tmux metadata is available. File viewer panels do not repeat tmux terminal details.
|
package/README.en.md
CHANGED
|
@@ -117,6 +117,8 @@ Defaults:
|
|
|
117
117
|
|
|
118
118
|
tmux mode still leaves batch barriers, `maxParallel`, final judge sequencing, and `judge_input.json` generation in Node.js. Each role output directory gets `run.sh` and `tmux.json`; status continues to be driven by `events.jsonl`, `stderr.log`, `last_message.md`, `exit_code`, and existing artifact files. After a tmux role command finishes, it writes `exit_code` first and then keeps the window open for inspection; the user closes the window manually from tmux.
|
|
119
119
|
|
|
120
|
+
If you are using `--runner tmux`, stopping and restarting `input-kanban serve` does not interrupt Codex sessions that are already running; the tmux session keeps going, and the scheduler resumes orchestration after the server comes back. With the `headless` runner, do not assume that restarting the service is safe for in-flight child processes.
|
|
121
|
+
|
|
120
122
|
tmux mode is optional and intended for live terminal viewing of each Codex role. `codex exec` is currently non-interactive and does not normally show manual approval prompts; if you select `danger-full-access` when creating a run, you explicitly relax the worker sandbox and should only do so in a controlled test workspace.
|
|
121
123
|
|
|
122
124
|
After run-level tmux metadata is available, the dashboard shows `Copy tmux attach command`. The file viewer no longer repeats tmux terminal details; use the run detail header to copy the attach command and inspect the tmux session.
|
package/README.md
CHANGED
|
@@ -117,6 +117,8 @@ input-kanban --open
|
|
|
117
117
|
|
|
118
118
|
tmux 模式仍由 Node.js 负责 batch barrier、`maxParallel`、final judge 顺序和 `judge_input.json` 生成。每个角色输出目录会写入 `run.sh` 和 `tmux.json`,状态继续由 `events.jsonl`、`stderr.log`、`last_message.md`、`exit_code` 和既有 artifact 文件驱动。tmux 角色命令完成后会先写入 `exit_code`,再保留 window,方便查看现场;需要关闭时由用户在 tmux 里手动退出。
|
|
119
119
|
|
|
120
|
+
如果当前使用的是 `--runner tmux`,中断并重新启动 `input-kanban serve` 不会中断正在执行中的 Codex 会话;tmux session 会继续运行,服务重启后 scheduler 会重新接管后续推进。若使用 `headless` runner,则不应假设服务重启对正在运行的子进程是安全的。
|
|
121
|
+
|
|
120
122
|
tmux 模式是可选能力,主要用于在终端里实时查看每个 Codex 角色的执行过程。`codex exec` 当前属于非交互模式,默认不会弹出人工 approval;如果创建任务时选择 `danger-full-access`,表示显式放开 worker sandbox 限制,应只在受控测试工作区中使用。
|
|
121
123
|
|
|
122
124
|
看板会在 run 生成 tmux 元数据后显示 `复制tmux attach指令`。文件查看区域不再重复展示 tmux 终端信息;如需查看现场,请从批次详情顶部复制 attach 指令进入 tmux session。
|
package/RELEASE_NOTES.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# Release Notes
|
|
2
2
|
|
|
3
|
+
## v0.0.12
|
|
4
|
+
|
|
5
|
+
### Highlights
|
|
6
|
+
|
|
7
|
+
- Fix Windows startup/static serving by resolving `APP_ROOT` with `fileURLToPath(import.meta.url)` instead of URL pathname parsing.
|
|
8
|
+
- Add a regression test for serving `/` and `/api/health` from the HTTP server.
|
|
9
|
+
- Add task-detail hover guidance for sandbox and network capability issues, clarifying that sandbox-denied errors are not necessarily task failures.
|
|
10
|
+
- Remember the last selected Web worker sandbox mode in browser local storage, so users do not need to reselect `danger-full-access` or other modes each time.
|
|
11
|
+
- Auto-scroll the execution process view to the end when opened, while preserving the user's scroll position during refresh if they have scrolled upward.
|
|
12
|
+
|
|
13
|
+
### Verification
|
|
14
|
+
|
|
15
|
+
- `npm run check` passed with 64 tests.
|
|
16
|
+
- `npm pack --dry-run` passed before release prep.
|
|
17
|
+
|
|
18
|
+
## v0.0.11
|
|
19
|
+
|
|
20
|
+
### Highlights
|
|
21
|
+
|
|
22
|
+
- Simplify the Web sidebar header: show `任务批次` as the section title with a compact `新建` action on the right, removing repeated wording.
|
|
23
|
+
- Document safe `input-kanban serve` restarts for `tmux` runner: already-running Codex sessions in tmux continue while the server is down, and the scheduler resumes after restart.
|
|
24
|
+
- Clarify that `headless` runner does not provide the same safe-restart guarantee for in-flight child processes.
|
|
25
|
+
|
|
26
|
+
### Verification
|
|
27
|
+
|
|
28
|
+
- `npm run check` passed with 63 tests.
|
|
29
|
+
- `npm pack --dry-run` passed before release prep.
|
|
30
|
+
|
|
3
31
|
## v0.0.10
|
|
4
32
|
|
|
5
33
|
### Highlights
|
package/package.json
CHANGED
package/public/index.html
CHANGED
|
@@ -60,12 +60,14 @@
|
|
|
60
60
|
.muted { color: var(--muted); font-size: 12px; }
|
|
61
61
|
.hidden { display: none; }
|
|
62
62
|
.toolbar { margin: 8px 0 12px; display: flex; flex-wrap: wrap; gap: 4px; align-items: center; }
|
|
63
|
+
.section-header { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin: 10px 0 8px; }
|
|
64
|
+
.section-header h2 { margin: 0; }
|
|
63
65
|
.workspace-filter-panel { margin: 4px 0 10px; display: flex; align-items: center; gap: 6px; }
|
|
64
66
|
.workspace-filter-select { flex: 1 1 auto; min-width: 0; font-size: 12px; padding: 7px 9px; color: #cbd5e1; }
|
|
65
67
|
.task-text { max-height: 180px; color: #cbd5e1; }
|
|
66
68
|
.empty { color: var(--muted); padding: 18px 0; }
|
|
67
|
-
.runs-load-icon { flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; width: 14px; height: 14px; border: 1px solid var(--line); border-radius: 999px; color: var(--muted); font-size: 10px; cursor: help; opacity: .72; }
|
|
68
|
-
.runs-load-icon:hover { opacity: 1; color: #cbd5e1; border-color: var(--line-strong); }
|
|
69
|
+
.info-icon, .runs-load-icon { flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; width: 14px; height: 14px; border: 1px solid var(--line); border-radius: 999px; color: var(--muted); font-size: 10px; cursor: help; opacity: .72; }
|
|
70
|
+
.info-icon:hover, .runs-load-icon:hover { opacity: 1; color: #cbd5e1; border-color: var(--line-strong); }
|
|
69
71
|
.run-list { display: flex; flex-direction: column; gap: 10px; flex: 1; min-height: 0; overflow-y: auto; padding-right: 4px; }
|
|
70
72
|
.run-list-more { width: 100%; margin-top: 4px; }
|
|
71
73
|
.run-card { border: 1px solid var(--line); border-radius: 12px; padding: 12px; background: var(--panel-2); cursor: pointer; transition: border-color .15s, transform .15s, background .15s; }
|
|
@@ -131,10 +133,10 @@
|
|
|
131
133
|
<main>
|
|
132
134
|
<div class="sidebar">
|
|
133
135
|
<section>
|
|
134
|
-
<div class="
|
|
135
|
-
<
|
|
136
|
+
<div class="section-header">
|
|
137
|
+
<h2>任务批次</h2>
|
|
138
|
+
<button class="secondary" onclick="showCreateForm()">新建</button>
|
|
136
139
|
</div>
|
|
137
|
-
<h2>任务批次</h2>
|
|
138
140
|
<div class="workspace-filter-panel">
|
|
139
141
|
<select id="workspaceFilterSelect" class="workspace-filter-select" onchange="setWorkspaceFilter(this.value)" title="未筛选工作区"></select>
|
|
140
142
|
<span id="runsLoadHint" class="runs-load-icon" title="批次列表尚未加载" aria-label="批次列表尚未加载">ⓘ</span>
|
|
@@ -154,7 +156,7 @@
|
|
|
154
156
|
<option value="read-only">read-only(只读)</option>
|
|
155
157
|
<option value="danger-full-access">danger-full-access(高风险,跳过沙箱限制)</option>
|
|
156
158
|
</select>
|
|
157
|
-
<div class="muted">仅影响 worker;任务拆分和汇总验收仍保持 read-only。</div>
|
|
159
|
+
<div class="muted">仅影响 worker;任务拆分和汇总验收仍保持 read-only。若执行过程提示 Permission denied / sandbox denied,这通常不是任务本身失败,而是当前沙箱能力不足;在可信工作区可改用 danger-full-access。DNS / 网络失败则通常需要检查代理、VPN 或本地 evidence。</div>
|
|
158
160
|
<label>任务说明</label><textarea id="taskText" placeholder="粘贴任务说明"></textarea>
|
|
159
161
|
<div class="toolbar">
|
|
160
162
|
<button onclick="createRun()">创建批次</button>
|
|
@@ -183,7 +185,10 @@
|
|
|
183
185
|
</section>
|
|
184
186
|
|
|
185
187
|
<section id="filePanel" class="log-panel">
|
|
186
|
-
<
|
|
188
|
+
<div class="section-header">
|
|
189
|
+
<h2>任务详情</h2>
|
|
190
|
+
<span class="info-icon" title="若执行过程提示 Permission denied / sandbox denied,这通常不是任务本身失败,而是当前 worker 沙箱能力不足;在可信工作区可改用 danger-full-access。DNS / 网络失败则通常需要检查代理、VPN 或本地 evidence。" aria-label="任务详情权限与网络提示">ⓘ</span>
|
|
191
|
+
</div>
|
|
187
192
|
<div id="fileTitle" class="muted">点击任务后查看详情</div>
|
|
188
193
|
<div id="fileTabs" class="toolbar file-tabs"></div>
|
|
189
194
|
<div id="executionSummary" class="execution-summary hidden"></div>
|
|
@@ -223,6 +228,8 @@ const statusByRunId = new Map();
|
|
|
223
228
|
const AUTO_REFRESH_MS = 3000;
|
|
224
229
|
const RUN_LIST_PAGE_SIZE = 10;
|
|
225
230
|
const WORKSPACE_FILTER_ALL = '';
|
|
231
|
+
const WORKER_SANDBOX_STORAGE_KEY = 'input-kanban.workerSandbox';
|
|
232
|
+
const VALID_WORKER_SANDBOXES = new Set(['read-only', 'workspace-write', 'danger-full-access']);
|
|
226
233
|
let currentWorkspacePath = '';
|
|
227
234
|
let selectedWorkspaceFilter = localStorage.getItem('input-kanban.workspaceFilter') || WORKSPACE_FILTER_ALL;
|
|
228
235
|
|
|
@@ -361,7 +368,20 @@ function hideCreateForm() {
|
|
|
361
368
|
document.getElementById('detailPanel').classList.remove('hidden');
|
|
362
369
|
document.getElementById('filePanel').classList.remove('hidden');
|
|
363
370
|
}
|
|
371
|
+
function saveWorkerSandboxPreference() {
|
|
372
|
+
const select = document.getElementById('workerSandbox');
|
|
373
|
+
const value = select?.value || '';
|
|
374
|
+
if (VALID_WORKER_SANDBOXES.has(value)) localStorage.setItem(WORKER_SANDBOX_STORAGE_KEY, value);
|
|
375
|
+
}
|
|
376
|
+
function initializeWorkerSandboxPreference() {
|
|
377
|
+
const select = document.getElementById('workerSandbox');
|
|
378
|
+
if (!select) return;
|
|
379
|
+
const saved = localStorage.getItem(WORKER_SANDBOX_STORAGE_KEY);
|
|
380
|
+
if (VALID_WORKER_SANDBOXES.has(saved)) select.value = saved;
|
|
381
|
+
select.addEventListener('change', saveWorkerSandboxPreference);
|
|
382
|
+
}
|
|
364
383
|
async function createRun() {
|
|
384
|
+
saveWorkerSandboxPreference();
|
|
365
385
|
const body = { label: label.value, workspace: repo.value, repo: repo.value, maxParallel: maxParallel.value, workerSandbox: workerSandbox.value, taskText: taskText.value };
|
|
366
386
|
const r = await api('/api/runs', { method: 'POST', body: JSON.stringify(body) });
|
|
367
387
|
selectedRun = r.runId; selectedTask = null; selectedFileName = null;
|
|
@@ -656,6 +676,7 @@ async function loadFile(name, { preserveScroll = false } = {}) {
|
|
|
656
676
|
selectedFileName = name;
|
|
657
677
|
const pre = document.getElementById('fileContent');
|
|
658
678
|
const previousScrollTop = pre.scrollTop;
|
|
679
|
+
const wasAtBottom = pre.scrollHeight - pre.scrollTop - pre.clientHeight < 24;
|
|
659
680
|
let text;
|
|
660
681
|
const selected = taskById(selectedTask);
|
|
661
682
|
if (name === 'result.json' && selected?.manualCompletion?.hasManualResult) {
|
|
@@ -665,7 +686,8 @@ async function loadFile(name, { preserveScroll = false } = {}) {
|
|
|
665
686
|
text = await api(`/api/runs/${selectedRun}/tasks/${selectedTask}/file?name=${encodeURIComponent(name)}`);
|
|
666
687
|
}
|
|
667
688
|
pre.textContent = text;
|
|
668
|
-
if (preserveScroll) pre.scrollTop =
|
|
689
|
+
if (name === 'events.pretty' && (!preserveScroll || wasAtBottom)) pre.scrollTop = pre.scrollHeight;
|
|
690
|
+
else if (preserveScroll) pre.scrollTop = previousScrollTop;
|
|
669
691
|
else pre.scrollTop = 0;
|
|
670
692
|
if (name === 'events.pretty') await renderExecutionSummary();
|
|
671
693
|
else hideExecutionSummary();
|
|
@@ -974,6 +996,7 @@ async function submitManualComplete() {
|
|
|
974
996
|
await loadFile('result.json');
|
|
975
997
|
}
|
|
976
998
|
|
|
999
|
+
initializeWorkerSandboxPreference();
|
|
977
1000
|
loadHealth().then(refreshRuns);
|
|
978
1001
|
setInterval(() => { if (selectedRun) refreshSelected({auto:true}).catch(console.error); else refreshRuns().catch(console.error); }, AUTO_REFRESH_MS);
|
|
979
1002
|
</script>
|
package/src/utils.js
CHANGED
|
@@ -3,11 +3,12 @@ import fsp from 'node:fs/promises';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import crypto from 'node:crypto';
|
|
5
5
|
import { createRequire } from 'node:module';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
6
7
|
|
|
7
8
|
const require = createRequire(import.meta.url);
|
|
8
9
|
const { version: PACKAGE_VERSION } = require('../package.json');
|
|
9
10
|
|
|
10
|
-
export const APP_ROOT = path.resolve(path.dirname(
|
|
11
|
+
export const APP_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
11
12
|
export { PACKAGE_VERSION };
|
|
12
13
|
export const DEFAULT_WORKSPACE = path.resolve(process.env.KANBAN_DEFAULT_WORKSPACE || process.env.KANBAN_DEFAULT_REPO || process.cwd());
|
|
13
14
|
export const DEFAULT_REPO = DEFAULT_WORKSPACE;
|