pipe-kan 0.34.0 → 0.34.2
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 +2 -0
- package/dist/pipe-kan.js +71 -28
- package/dist/ui/assets/index-D0nAKttP.js +56 -0
- package/dist/ui/index.html +1 -1
- package/package.json +1 -1
- package/dist/ui/assets/index-B4MA2yJG.js +0 -56
package/README.md
CHANGED
|
@@ -63,6 +63,8 @@ bunx pipe-kan --plane --projects APH,PULSE,PKAN,PUI,DEC
|
|
|
63
63
|
|
|
64
64
|
Same `--projects` style as Jira: comma-separated, trimmed, uppercased. CLI flags are startup defaults. The sidebar switcher (O then W) checks Sources, Workspaces, and Projects and refreshes the merged Board. The header project chip opens that same menu. Scope flags still round-trip the selection. Next launch uses argv, else `PLANE_WORKSPACE`, else `personal`.
|
|
65
65
|
|
|
66
|
+
Workspace discovery: Plane's public v1 API has no workspace-list endpoint, so the switcher lists every workspace the key can read by probing candidate slugs (startup flags, `PLANE_WORKSPACE`, `PLANE_WORKSPACES`, `PIPE_KAN_WORKSPACES`, plus `personal` and `work`) regardless of the startup scope. The startup flags bar is hidden by default; Display > Show startup flags bar brings it back.
|
|
67
|
+
|
|
66
68
|
Plane mode adds Initiatives and Projects. Initiatives are modules (health, status, priority, target date) in a list, board, or timeline. Projects show completion, target date, and health. Display is Shift+V. The Filter menu (F) adds view filters, chips, and saved views. Right-click a row for status, assignee, priority, labels, or copy link. Cmd+K jumps to those views; `ow` lists workspaces.
|
|
67
69
|
|
|
68
70
|
When more than one Workspace is selected, issue keys are `workspace/KEY` so `work/PULSE` and `personal/PULSE` stay distinct. Catalog reads are serialized, cached, and keep trailing slashes.
|
package/dist/pipe-kan.js
CHANGED
|
@@ -1841,6 +1841,7 @@ function sourceOptions(input) {
|
|
|
1841
1841
|
// src/plane.ts
|
|
1842
1842
|
var DEFAULT_PLANE_HOST = "https://plane.tail48fe8.ts.net";
|
|
1843
1843
|
var DEFAULT_PLANE_WORKSPACE = "personal";
|
|
1844
|
+
var COMMON_WORKSPACE_SLUGS = ["personal", "work"];
|
|
1844
1845
|
var STATE_GROUPS = ["backlog", "unstarted", "started", "completed", "cancelled"];
|
|
1845
1846
|
function planeHost(env = process.env) {
|
|
1846
1847
|
return (env.PLANE_HOST ?? DEFAULT_PLANE_HOST).replace(/\/$/, "");
|
|
@@ -2217,6 +2218,44 @@ function createPlaneCli(opts) {
|
|
|
2217
2218
|
return;
|
|
2218
2219
|
return { id: asString(row?.id) ?? slug, name: asString(row?.name) ?? slug, slug };
|
|
2219
2220
|
}
|
|
2221
|
+
const known = new Set;
|
|
2222
|
+
let probeCache;
|
|
2223
|
+
async function probeWorkspaces() {
|
|
2224
|
+
const ttl = opts.cacheTtlMs ?? 300000;
|
|
2225
|
+
if (ttl > 0 && probeCache && nowFn() - probeCache.at < ttl)
|
|
2226
|
+
return probeCache.rows;
|
|
2227
|
+
const rows = await probeWorkspacesUncached();
|
|
2228
|
+
probeCache = { at: nowFn(), rows };
|
|
2229
|
+
return rows;
|
|
2230
|
+
}
|
|
2231
|
+
async function probeWorkspacesUncached() {
|
|
2232
|
+
const env = opts.env ?? process.env;
|
|
2233
|
+
const fromFlags = parseFlags(`${defaultFlags} ${lastFlags}`);
|
|
2234
|
+
const candidates = new Set([
|
|
2235
|
+
...known,
|
|
2236
|
+
...fromFlags.workspaces,
|
|
2237
|
+
...fromFlags.workspace ? [fromFlags.workspace] : [],
|
|
2238
|
+
...fromFlags.scopes.map((scope) => scope.workspace),
|
|
2239
|
+
...env.PLANE_WORKSPACE ? [env.PLANE_WORKSPACE] : [],
|
|
2240
|
+
...(env.PLANE_WORKSPACES ?? "").split(","),
|
|
2241
|
+
...(env.PIPE_KAN_WORKSPACES ?? "").split(","),
|
|
2242
|
+
...COMMON_WORKSPACE_SLUGS
|
|
2243
|
+
]);
|
|
2244
|
+
const results = await Promise.all([...candidates].map(async (raw) => {
|
|
2245
|
+
const slug = raw.trim();
|
|
2246
|
+
if (!slug || !/^[A-Za-z0-9_-]+$/.test(slug))
|
|
2247
|
+
return;
|
|
2248
|
+
try {
|
|
2249
|
+
await requestJson(queryPath(`/workspaces/${slug}/projects/`, { per_page: "1" }));
|
|
2250
|
+
known.add(slug);
|
|
2251
|
+
return { id: slug, name: slug, slug };
|
|
2252
|
+
} catch {
|
|
2253
|
+
known.delete(slug);
|
|
2254
|
+
return;
|
|
2255
|
+
}
|
|
2256
|
+
}));
|
|
2257
|
+
return results.filter((row) => Boolean(row));
|
|
2258
|
+
}
|
|
2220
2259
|
function parseWorkspaceList(raw) {
|
|
2221
2260
|
const rows = Array.isArray(raw) ? raw : asRecord(raw)?.results;
|
|
2222
2261
|
if (!Array.isArray(rows))
|
|
@@ -2290,7 +2329,7 @@ function createPlaneCli(opts) {
|
|
|
2290
2329
|
};
|
|
2291
2330
|
}
|
|
2292
2331
|
const projectCache = new Map;
|
|
2293
|
-
const cacheTtlMs = opts.cacheTtlMs ??
|
|
2332
|
+
const cacheTtlMs = opts.cacheTtlMs ?? 300000;
|
|
2294
2333
|
async function projectsFor(slug) {
|
|
2295
2334
|
const hit = projectCache.get(slug);
|
|
2296
2335
|
const now = nowFn();
|
|
@@ -2560,17 +2599,17 @@ function createPlaneCli(opts) {
|
|
|
2560
2599
|
return loaded.columns;
|
|
2561
2600
|
},
|
|
2562
2601
|
async listWorkspaces() {
|
|
2602
|
+
const ttl = opts.cacheTtlMs ?? 300000;
|
|
2603
|
+
if (ttl > 0 && probeCache && nowFn() - probeCache.at < ttl)
|
|
2604
|
+
return probeCache.rows;
|
|
2563
2605
|
for (const path of ["/users/me/workspaces/", "/workspaces/"]) {
|
|
2564
2606
|
try {
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
continue;
|
|
2570
|
-
return [];
|
|
2571
|
-
}
|
|
2607
|
+
const listed = parseWorkspaceList(await requestJson(path));
|
|
2608
|
+
if (listed?.length)
|
|
2609
|
+
return listed;
|
|
2610
|
+
} catch {}
|
|
2572
2611
|
}
|
|
2573
|
-
return
|
|
2612
|
+
return probeWorkspaces();
|
|
2574
2613
|
},
|
|
2575
2614
|
async listProjectIdentifiers(workspace) {
|
|
2576
2615
|
const slug = workspace.trim();
|
|
@@ -2740,6 +2779,7 @@ async function createBoardApp(opts) {
|
|
|
2740
2779
|
apiKey: env.PLANE_API_KEY ?? "",
|
|
2741
2780
|
workspace: planeWorkspace(flags, env),
|
|
2742
2781
|
flags,
|
|
2782
|
+
env,
|
|
2743
2783
|
fetch: opts.fetch ?? resolvePlaneFetch(env)
|
|
2744
2784
|
}) : useJira ? createJiraCli({
|
|
2745
2785
|
bin,
|
|
@@ -14172,9 +14212,15 @@ function handleRequest(req, res, ctx) {
|
|
|
14172
14212
|
if (handleAppApi(req, res, ctx.app, { kind: ctx.kind, env: ctx.env, onSource: ctx.switchSource }) || handleAgentApi(req, res, ctx.app)) {
|
|
14173
14213
|
return true;
|
|
14174
14214
|
}
|
|
14175
|
-
if (ctx.kind
|
|
14176
|
-
return
|
|
14177
|
-
|
|
14215
|
+
if (ctx.kind !== "plane" && handleFakeJira(req, res, ctx.store))
|
|
14216
|
+
return true;
|
|
14217
|
+
if ((req.url ?? "").split("?")[0].startsWith("/api/")) {
|
|
14218
|
+
res.statusCode = 404;
|
|
14219
|
+
res.setHeader("content-type", "application/json");
|
|
14220
|
+
res.end(JSON.stringify({ error: "unknown API route" }));
|
|
14221
|
+
return true;
|
|
14222
|
+
}
|
|
14223
|
+
return false;
|
|
14178
14224
|
}
|
|
14179
14225
|
|
|
14180
14226
|
// src/jira-config.ts
|
|
@@ -14315,12 +14361,12 @@ async function runServer(opts) {
|
|
|
14315
14361
|
live.store = next.store;
|
|
14316
14362
|
live.kind = next.kind;
|
|
14317
14363
|
if (next.kind === "plane" || next.kind === "jira") {
|
|
14318
|
-
|
|
14319
|
-
|
|
14320
|
-
|
|
14321
|
-
|
|
14322
|
-
|
|
14323
|
-
}
|
|
14364
|
+
const app = live.app;
|
|
14365
|
+
const kind = next.kind;
|
|
14366
|
+
refreshFromJira(app, kind).catch((err) => {
|
|
14367
|
+
console.error(`${kind} Refresh after source switch failed`, err);
|
|
14368
|
+
});
|
|
14369
|
+
return { ok: true, kind: live.kind, source, refreshing: true };
|
|
14324
14370
|
}
|
|
14325
14371
|
return { ok: true, kind: live.kind, source };
|
|
14326
14372
|
} catch (err) {
|
|
@@ -14340,20 +14386,17 @@ async function runServer(opts) {
|
|
|
14340
14386
|
res.end("pipe-kan UI is missing. Run bun run build.");
|
|
14341
14387
|
}
|
|
14342
14388
|
});
|
|
14343
|
-
if (live.kind === "plane") {
|
|
14344
|
-
try {
|
|
14345
|
-
await refreshFromJira(live.app, live.kind, { piped: Boolean(piped) });
|
|
14346
|
-
} catch (err) {
|
|
14347
|
-
console.error(`${live.kind} Refresh failed`, err);
|
|
14348
|
-
}
|
|
14349
|
-
const error = live.app.board().error;
|
|
14350
|
-
if (error)
|
|
14351
|
-
console.error(`${live.kind} Refresh failed; keeping last live Board`, error);
|
|
14352
|
-
}
|
|
14353
14389
|
const { host, port } = resolveListen();
|
|
14354
14390
|
await bindListen(server, host, port);
|
|
14355
14391
|
const origin = `http://127.0.0.1:${port}`;
|
|
14356
14392
|
announce(`pipe-kan http://${host}:${port}`);
|
|
14393
|
+
if (live.kind === "plane") {
|
|
14394
|
+
refreshFromJira(live.app, live.kind, { piped: Boolean(piped) }).catch((err) => console.error(`${live.kind} Refresh failed`, err)).then(() => {
|
|
14395
|
+
const error = live.app.board().error;
|
|
14396
|
+
if (error)
|
|
14397
|
+
console.error(`${live.kind} Refresh failed; keeping last live Board`, error);
|
|
14398
|
+
});
|
|
14399
|
+
}
|
|
14357
14400
|
if (live.kind === "plane") {
|
|
14358
14401
|
announce(`cli plane ${planeHost()} ${planeWorkspace(flags)}`);
|
|
14359
14402
|
announce(`Plane host default ${DEFAULT_PLANE_HOST}`);
|