@takagaki/cortex-decisions-viewer 0.4.77 → 0.4.79
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/render.js +237 -25
- package/package.json +1 -1
package/dist/render.js
CHANGED
|
@@ -356,6 +356,20 @@ function capabilityState(cap, ctx) {
|
|
|
356
356
|
// 途中で壊れたデータで**画面が落ちない**ようにするためだけ——実体(同期ソースの行・
|
|
357
357
|
// ライブ参照)だけで判定し、無ければ「まだ繋がっていない」側へ倒す。
|
|
358
358
|
// **ここに宣言を読む道を足さないこと。** 足した瞬間、実態と食い違える経路が復活する。
|
|
359
|
+
// **fleet のデータが1枚も無いときは「未接続」(#138)。** 下の判定は
|
|
360
|
+
// 「行が無い=使っていない」と読んで `off`(未使用)を返すが、立ち上げ直後の案件は
|
|
361
|
+
// まだ夜間バッチが一度も走っていないだけで、何も決めていない。6つの能力に
|
|
362
|
+
// 「未使用」が並ぶと、**繋ぐ入口を出すという目的と逆のことを画面が言う**
|
|
363
|
+
// (「使わないことになっている」と読める)。
|
|
364
|
+
//
|
|
365
|
+
// **分岐は「1つも無い」に限る。** この防御経路は取得に失敗したときにも通るので、
|
|
366
|
+
// 判定材料が1つでもあるなら従来どおり——失敗を「未接続」に塗り替えない。
|
|
367
|
+
if (ctx.internals.length === 0 &&
|
|
368
|
+
ctx.externals.length === 0 &&
|
|
369
|
+
ctx.pipes.length === 0 &&
|
|
370
|
+
Object.keys(ctx.capabilities || {}).length === 0) {
|
|
371
|
+
return "idle";
|
|
372
|
+
}
|
|
359
373
|
const used = (internal != null && internal.enabled !== false) ||
|
|
360
374
|
(isLive && liveExts.length > 0);
|
|
361
375
|
const off = (internal != null && internal.enabled === false) || !used;
|
|
@@ -429,11 +443,17 @@ function renderConnectionMap(site) {
|
|
|
429
443
|
const externals = site.externalSources ?? [];
|
|
430
444
|
const pipes = site.pipelines ?? [];
|
|
431
445
|
const caps = site.capabilities ?? {};
|
|
432
|
-
//
|
|
433
|
-
// 素の Decisions
|
|
434
|
-
// そのまま。宣言(`tools
|
|
446
|
+
// **「この画面から接続できる構成か」で判定する(#138)。**
|
|
447
|
+
// 素の Decisions ビュー(投入フォームを持たない構成)ではマップを出さない現行の意図は
|
|
448
|
+
// そのまま。宣言(`tools`)は条件から外してある(#125⑤)——艦隊の fleet-status.json は
|
|
435
449
|
// すべて capabilities を持つので、宣言はもうマップの有無を左右しない。
|
|
436
|
-
|
|
450
|
+
//
|
|
451
|
+
// **中身が空でも、intake があるならマップを出す。** 立ち上げ直後の案件は fleet の
|
|
452
|
+
// データが丸ごと無いが、そこは**まさにこれから繋ぐ案件**で、6つの能力とその入口を
|
|
453
|
+
// 一望できる図が一番要る場面だった。中身の有無で消していたので、新規案件の画面からは
|
|
454
|
+
// 「何を繋げばよいのか」を示すものが消えていた。
|
|
455
|
+
if (!site.intake &&
|
|
456
|
+
!internals.length && !externals.length && !pipes.length && !Object.keys(caps).length) {
|
|
437
457
|
return null;
|
|
438
458
|
}
|
|
439
459
|
const hasCaps = Object.keys(caps).length > 0;
|
|
@@ -1329,6 +1349,8 @@ a.ds-name:hover { text-decoration: underline; }
|
|
|
1329
1349
|
.rt-steps li { margin: 2px 0; }
|
|
1330
1350
|
/* URLは長いので、入力欄を伸ばしてボタンを右に置く(折り返しても崩れないように wrap) */
|
|
1331
1351
|
.rt-url-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
|
|
1352
|
+
.rt-copy { margin: 6px 0 10px; }
|
|
1353
|
+
.rt-copy-text { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; background: #f3f4f6; border: 1px solid #e5e7eb; border-radius: 6px; padding: 4px 10px; user-select: all; }
|
|
1332
1354
|
.rt-url { flex: 1 1 260px; margin-bottom: 0; font-family: var(--mono, ui-monospace, SFMono-Regular, Menlo, monospace); font-size: 12px; }
|
|
1333
1355
|
/* 稼働中の表示(#123)。緑は「動いている」の合図で、見出しの右に置いて先に目に入れる */
|
|
1334
1356
|
.rt-live-badge {
|
|
@@ -1543,6 +1565,46 @@ ${capabilityState.toString()}
|
|
|
1543
1565
|
footerEl.textContent = "生成: " + SITE.generatedAt +
|
|
1544
1566
|
(SITE.repoBaseUrl ? " / " + SITE.repoBaseUrl.split("://").pop() + " (" + SITE.branch + ")" : "");
|
|
1545
1567
|
|
|
1568
|
+
/**
|
|
1569
|
+
* 「貼り付けて使う文字列」を、選べる形+コピーボタンで出す。
|
|
1570
|
+
*
|
|
1571
|
+
* **手で打たせない。** 「/invite @Cortex」のような短いものでも、打ち間違いは黙って失敗し、
|
|
1572
|
+
* 利用者には「招待したのに読まれない」としか見えない。
|
|
1573
|
+
* clipboard API が使えない環境(http・古いブラウザ・権限拒否)では、**選択までしてやって**
|
|
1574
|
+
* 次の一手を伝える(何も起きないのが一番悪い)。
|
|
1575
|
+
*/
|
|
1576
|
+
function copyRow(text) {
|
|
1577
|
+
// **input と button を使わない。** 追加フォームの中に置くので、input を足すと
|
|
1578
|
+
// 「先頭がURL欄・末尾が名前欄」、button を足すと「最初のボタンが送信」という
|
|
1579
|
+
// フォームの並びが崩れる(実際にテストが壊れて気づいた)。
|
|
1580
|
+
// 表示は code、コピーは role="button" のリンクで代える。
|
|
1581
|
+
var field = el("code", { class: "rt-copy-text", text: text });
|
|
1582
|
+
var btn = el("a", { class: "ds-toggle-btn", role: "button", tabindex: "0", text: "コピー" });
|
|
1583
|
+
var msg = el("div", { class: "ds-toggle-msg" });
|
|
1584
|
+
btn.addEventListener("click", function () {
|
|
1585
|
+
var done = function () { msg.className = "ds-toggle-msg is-ok"; msg.textContent = "✓ コピーしました。"; };
|
|
1586
|
+
var fallback = function () {
|
|
1587
|
+
// clipboard が使えない環境では、せめて**文字列を選択状態にして**次の一手を伝える
|
|
1588
|
+
try {
|
|
1589
|
+
var r = document.createRange();
|
|
1590
|
+
r.selectNodeContents(field);
|
|
1591
|
+
var sel = window.getSelection();
|
|
1592
|
+
sel.removeAllRanges();
|
|
1593
|
+
sel.addRange(r);
|
|
1594
|
+
if (document.execCommand && document.execCommand("copy")) return done();
|
|
1595
|
+
} catch (e) { /* 下の案内へ */ }
|
|
1596
|
+
msg.className = "ds-toggle-msg is-error";
|
|
1597
|
+
msg.textContent = "⚠ 自動コピーできませんでした。選択された文字列を手でコピーしてください。";
|
|
1598
|
+
};
|
|
1599
|
+
try {
|
|
1600
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
1601
|
+
navigator.clipboard.writeText(text).then(done, fallback);
|
|
1602
|
+
} else fallback();
|
|
1603
|
+
} catch (e) { fallback(); }
|
|
1604
|
+
});
|
|
1605
|
+
return el("div", { class: "rt-copy" }, [el("div", { class: "rt-url-row" }, [field, btn]), msg]);
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1546
1608
|
function el(tag, props, children) {
|
|
1547
1609
|
var e = document.createElement(tag);
|
|
1548
1610
|
if (props) Object.keys(props).forEach(function (k) {
|
|
@@ -1947,7 +2009,9 @@ ${capabilityState.toString()}
|
|
|
1947
2009
|
};
|
|
1948
2010
|
var DS_GATE = {
|
|
1949
2011
|
"ok": { text: "接続OK", cls: "ds-gate-ok" },
|
|
1950
|
-
|
|
2012
|
+
// **状態だけでなく、次にすることを書く。** 「bot未招待」では何をすればよいか分からない
|
|
2013
|
+
// (公開チャンネルでも招待が要ることは Slack の仕様で、知らない人のほうが多い)
|
|
2014
|
+
"not_in_channel": { text: "Slackで招待してください", cls: "ds-gate-warn" },
|
|
1951
2015
|
"unreachable": { text: "アクセス不可", cls: "ds-gate-warn" },
|
|
1952
2016
|
"no_token": { text: "トークン未設定", cls: "ds-gate-muted" },
|
|
1953
2017
|
"unknown": { text: "不明", cls: "ds-gate-muted" },
|
|
@@ -2033,6 +2097,18 @@ ${capabilityState.toString()}
|
|
|
2033
2097
|
return row;
|
|
2034
2098
|
}
|
|
2035
2099
|
|
|
2100
|
+
/**
|
|
2101
|
+
* GitHub App のインストール先(#138)。
|
|
2102
|
+
*
|
|
2103
|
+
* **1箇所に持つ。** 組織ごとの行と、読み取りトークンのカードの両方から出すので、
|
|
2104
|
+
* 書き写すと片方だけが古くなる。
|
|
2105
|
+
*
|
|
2106
|
+
* **文書化されている形だけを使う。** target_id に組織のログイン名を渡す形は GitHub の
|
|
2107
|
+
* 文書に無く、未ログインだとログイン画面へ転送されて 200 が返るので、効いているかを
|
|
2108
|
+
* 外から確かめられない。**確かめられないものを顧客に押させない。**
|
|
2109
|
+
*/
|
|
2110
|
+
var GITHUB_APP_INSTALL_URL = "https://github.com/apps/classmethod-cortex-app/installations/new";
|
|
2111
|
+
|
|
2036
2112
|
/**
|
|
2037
2113
|
* 接続状況のライブ値(op=state の応答)。取得前・取得失敗時は null。
|
|
2038
2114
|
*
|
|
@@ -2174,6 +2250,9 @@ ${capabilityState.toString()}
|
|
|
2174
2250
|
// 「これから繋げる入口」も接続マップのアンカーの受け皿も無くなる。
|
|
2175
2251
|
// 何を繋げるかは製品が決めていて、案件データでは変わらない(#125②)。
|
|
2176
2252
|
var CAP_ORDER = ["課題管理", "会議", "共有資料", "チャット", "デザイン", "開発"];
|
|
2253
|
+
// internalSources に行が来る能力(チャットと開発は externalSources 側にしか来ない)。
|
|
2254
|
+
// **fleet-status.json がまだ無い案件のために、行が無くても設定を出す**判定に使う(#138)
|
|
2255
|
+
var CAP_INTERNAL = ["課題管理", "会議", "共有資料", "デザイン"];
|
|
2177
2256
|
var capBox = {};
|
|
2178
2257
|
var manualBox = {};
|
|
2179
2258
|
// 対応表は externalCapability が持つ(状態判定・件数の集計と同じものを使う)
|
|
@@ -2290,6 +2369,26 @@ ${capabilityState.toString()}
|
|
|
2290
2369
|
});
|
|
2291
2370
|
}
|
|
2292
2371
|
|
|
2372
|
+
// **行が1つも無い能力にも、設定の口を出す(#138)。**
|
|
2373
|
+
//
|
|
2374
|
+
// 実機で踏んだ欠陥の本体はここだった。各能力の設定UI一式は internalControls の中に
|
|
2375
|
+
// あり、その呼び出しは上の internals.forEach の内側だけ。行を作っているのは夜間バッチ
|
|
2376
|
+
// (fleet-status.json)なので、**立ち上げ直後の案件には1行も無く、タイルを開いても
|
|
2377
|
+
// 「この能力にはまだ設定できるものがありません。」で終わる**——接続先のフォームを
|
|
2378
|
+
// 出す・出さないの判定を直しても、その手前で画面に到達できていなかった。
|
|
2379
|
+
//
|
|
2380
|
+
// **データの行(カード)は作らない。** 作れば、読めていない件数・最終同期・ラベルを
|
|
2381
|
+
// 画面が装うことになる。ここで足すのは操作だけで、状態の表示はタイルとマップが持つ。
|
|
2382
|
+
//
|
|
2383
|
+
// 状態が分からないと出せない操作(会議・共有資料のトグルや追加フォーム)は
|
|
2384
|
+
// internalControls 側の判断で従来どおり出ない——不明な状態に操作を出さない原則は
|
|
2385
|
+
// 変えていない。ここが変えるのは「呼ばれもしなかった」ことだけ。
|
|
2386
|
+
CAP_INTERNAL.forEach(function (cap) {
|
|
2387
|
+
if (internals.filter(function (s) { return s.kind === cap; }).length) return;
|
|
2388
|
+
var ctl = internalControls({ kind: cap }, false);
|
|
2389
|
+
if (ctl) pushCap(cap, ctl);
|
|
2390
|
+
});
|
|
2391
|
+
|
|
2293
2392
|
if (sources.length) {
|
|
2294
2393
|
// **見出しは付けない。** #101 で行は能力ごとのモーダルへ移ったので、
|
|
2295
2394
|
// ここに見出しを置くと中身ゼロの見出しが節の先頭に立つ(実際に8案件すべてで出ていた)
|
|
@@ -2383,12 +2482,9 @@ ${capabilityState.toString()}
|
|
|
2383
2482
|
if (!ok && !unsure) {
|
|
2384
2483
|
row.appendChild(el("a", {
|
|
2385
2484
|
class: "ds-owner-install",
|
|
2386
|
-
//
|
|
2387
|
-
//
|
|
2388
|
-
|
|
2389
|
-
// 効いているかを外から確かめられない。**確かめられないものを顧客に押させない。**
|
|
2390
|
-
// どの組織かはこの行に出ているので、選ばせる形で足りる
|
|
2391
|
-
href: "https://github.com/apps/classmethod-cortex-app/installations/new",
|
|
2485
|
+
// どの組織かはこの行に出ているので、選ばせる形で足りる(URLの形は
|
|
2486
|
+
// GITHUB_APP_INSTALL_URL のコメントを参照)
|
|
2487
|
+
href: GITHUB_APP_INSTALL_URL,
|
|
2392
2488
|
target: "_blank", rel: "noopener",
|
|
2393
2489
|
text: "この組織にインストール →",
|
|
2394
2490
|
}));
|
|
@@ -2396,6 +2492,24 @@ ${capabilityState.toString()}
|
|
|
2396
2492
|
obox.appendChild(row);
|
|
2397
2493
|
});
|
|
2398
2494
|
pushCap("開発", obox);
|
|
2495
|
+
} else {
|
|
2496
|
+
// **組織が1件も無い案件にも、App の入口を置く(#138)。**
|
|
2497
|
+
// githubOwners は夜間バッチの成果物なので、立ち上げ直後の案件には無い。
|
|
2498
|
+
// インストールの導線を「未接続の組織の行」の中にしか置いていなかったため、
|
|
2499
|
+
// **まだ何も繋いでいない案件=一番 App を入れてほしい案件**の画面に、
|
|
2500
|
+
// App へ行く道が1本も無かった(実機で踏んだ)。
|
|
2501
|
+
var obox0 = el("div", { class: "ds-owners" });
|
|
2502
|
+
obox0.appendChild(el("div", { class: "ds-sub", text: "組織ごとの接続状況" }));
|
|
2503
|
+
obox0.appendChild(el("div", { class: "ds-owner" }, [
|
|
2504
|
+
el("span", { class: "ds-owner-state ds-gate-idle", text: "⦿ 未接続 読み取っている組織はまだありません" }),
|
|
2505
|
+
el("a", {
|
|
2506
|
+
class: "ds-owner-install",
|
|
2507
|
+
href: GITHUB_APP_INSTALL_URL,
|
|
2508
|
+
target: "_blank", rel: "noopener",
|
|
2509
|
+
text: "組織に GitHub App をインストール →",
|
|
2510
|
+
}),
|
|
2511
|
+
]));
|
|
2512
|
+
pushCap("開発", obox0);
|
|
2399
2513
|
}
|
|
2400
2514
|
|
|
2401
2515
|
pushCap("開発", adders.querySelector('[data-adder="github"]'));
|
|
@@ -2414,10 +2528,19 @@ ${capabilityState.toString()}
|
|
|
2414
2528
|
*
|
|
2415
2529
|
* **unknown(確かめられなかった組織)も「要る」側に倒す。** 実態は none かもしれず、
|
|
2416
2530
|
* 隠す方に倒すと直せなくなる(画面には組織ごとに「確認できませんでした」と出ている)。
|
|
2531
|
+
*
|
|
2532
|
+
* **「無い」と「空」は別物(#138・オーナー判断 2026-08-18)。**
|
|
2533
|
+
* - 値そのものが来ていない(undefined)… fleet-status.json をまだ持っていない、または
|
|
2534
|
+
* 取得に失敗した。**情報が無いので出す**(上の #118 の意図はここが担う)。
|
|
2535
|
+
* - 空の配列([])… 夜間バッチは走ったが、読み取り対象の開発リポジトリが0件。
|
|
2536
|
+
* 読むものが無いのだから鍵も要らない。**出さない**。
|
|
2537
|
+
* 以前はどちらも空配列に丸めていたので、0件の案件にも欄が出続けていた。
|
|
2538
|
+
* **この2つを混ぜると #118 を壊す**(立ち上げ直後の案件から唯一の入口が消える)。
|
|
2417
2539
|
*/
|
|
2418
2540
|
function githubTokenNeeded() {
|
|
2419
|
-
var list = fleetVal("githubOwners")
|
|
2420
|
-
if (!list
|
|
2541
|
+
var list = fleetVal("githubOwners");
|
|
2542
|
+
if (!Array.isArray(list)) return true;
|
|
2543
|
+
if (!list.length) return false;
|
|
2421
2544
|
return list.some(function (o) { return !o || (o.source !== "app" && o.source !== "token"); });
|
|
2422
2545
|
}
|
|
2423
2546
|
|
|
@@ -2428,9 +2551,16 @@ ${capabilityState.toString()}
|
|
|
2428
2551
|
// 全組織が繋がっているなら、入力欄ではなく**そう言う1行**に畳む(#118)。
|
|
2429
2552
|
// 何も出さないと「トークンはどこで入れるのか」が分からなくなる
|
|
2430
2553
|
if (k === "external-sources" && !githubTokenNeeded()) {
|
|
2554
|
+
// **0件の案件に「すべての組織に接続済み」と言わない(#138)。** 組織が1つも無いのに
|
|
2555
|
+
// 接続済みと書けば嘘になる。畳む理由が違うので、文も分ける——次の一歩
|
|
2556
|
+
//(リポジトリを登録する)が同じモーダルの上にあることまで書く
|
|
2557
|
+
var ownersNow = fleetVal("githubOwners");
|
|
2558
|
+
var noRepos = Array.isArray(ownersNow) && ownersNow.length === 0;
|
|
2431
2559
|
pushCap(SECRET_CAP[k], el("p", {
|
|
2432
2560
|
class: "sec-how ds-all-connected",
|
|
2433
|
-
text:
|
|
2561
|
+
text: noRepos
|
|
2562
|
+
? "読み取り対象の開発リポジトリがまだありません。このモーダルの上にあるリポジトリの追加フォームで登録すると、トークンが要るかどうかをここに出します。"
|
|
2563
|
+
: "すべての組織に接続済みです(GitHub App / 登録済みトークン)。読み取りトークンの登録は要りません。",
|
|
2434
2564
|
}));
|
|
2435
2565
|
return;
|
|
2436
2566
|
}
|
|
@@ -2936,6 +3066,47 @@ ${capabilityState.toString()}
|
|
|
2936
3066
|
var TARGETS_RACE_NOTE =
|
|
2937
3067
|
"他の人が先に接続先を変更しました。最新の一覧に更新しましたので、内容を確かめてもう一度お試しください。";
|
|
2938
3068
|
|
|
3069
|
+
/**
|
|
3070
|
+
* この画面から Backlog の接続先を足したり外したりできるか(#138)。
|
|
3071
|
+
*
|
|
3072
|
+
* **同期ミラー(s.url)では判定しない。** 以前は「一度でも同期していれば書き込み先が
|
|
3073
|
+
* 決まる」として s.url を見ていたが、正本(issues-config.json)は雛形から作られるので
|
|
3074
|
+
* 立ち上げ直後の案件にも実在する。ミラーで判定すると、**まだ一度も繋いでいない案件=
|
|
3075
|
+
* これから繋ぎたい案件**だけがフォームを出せない(実機で踏んだ)。
|
|
3076
|
+
*
|
|
3077
|
+
* 出さないのは、この画面からの投入に対応していない固定IP同期の案件だけ。
|
|
3078
|
+
* **その見分けは SITE.secretKinds に backlog が入っているかで行う。**
|
|
3079
|
+
* 固定IPかどうかを直接示す値は画面に来ておらず(インフラ側の設定にしか無い)、
|
|
3080
|
+
* 鍵の欄を出すかどうかの判定が同じ線で引かれているので、代理として正確に使える。
|
|
3081
|
+
* インフラ側で secretKinds の条件を変えるときは、**ここの意味も一緒に変わる**。
|
|
3082
|
+
*/
|
|
3083
|
+
function backlogTargetsEditable() {
|
|
3084
|
+
return (SITE.secretKinds || []).indexOf("backlog") >= 0;
|
|
3085
|
+
}
|
|
3086
|
+
|
|
3087
|
+
/**
|
|
3088
|
+
* Backlog のAPIキーを、どのスペースの棚へ入れるか(#138)。
|
|
3089
|
+
*
|
|
3090
|
+
* **名簿(config.ts)の backlogSpace に頼らない。** 立ち上げ直後の案件は接続先を
|
|
3091
|
+
* まだ名簿に書いていないので、サーバ側だけではスペースが決まらない——鍵の欄を出しても
|
|
3092
|
+
* 保存が断られる(#127 で「2つ目以降のスペースの鍵は画面から名乗ってもらう」経路が
|
|
3093
|
+
* 用意してあるので、そこへ載せる)。
|
|
3094
|
+
*
|
|
3095
|
+
* **名乗るのは1つに定まるときだけ。** 画面の接続先が複数のスペースにまたがっているなら、
|
|
3096
|
+
* どちらの鍵として入れたのかは入れた人にしか分からない。勝手に片方を選ぶと、
|
|
3097
|
+
* **読み手が見に行くのと別の棚へ黙って入る**(入れたのに動かない、が一番起きてほしくない形)。
|
|
3098
|
+
* 名乗らなければサーバが従来どおり名簿の値を使う。
|
|
3099
|
+
*/
|
|
3100
|
+
function backlogSecretSpace() {
|
|
3101
|
+
var list = backlogTargetsNow(null);
|
|
3102
|
+
var domains = [];
|
|
3103
|
+
for (var i = 0; i < list.length; i++) {
|
|
3104
|
+
var d = String(list[i].domain || "").toLowerCase();
|
|
3105
|
+
if (d && domains.indexOf(d) === -1) domains.push(d);
|
|
3106
|
+
}
|
|
3107
|
+
return domains.length === 1 ? domains[0] : "";
|
|
3108
|
+
}
|
|
3109
|
+
|
|
2939
3110
|
/**
|
|
2940
3111
|
* 「課題管理」カードの接続先(一覧+追加+削除・#127)。
|
|
2941
3112
|
*
|
|
@@ -2949,9 +3120,7 @@ ${capabilityState.toString()}
|
|
|
2949
3120
|
*/
|
|
2950
3121
|
function backlogTargetsSection(s) {
|
|
2951
3122
|
var targets = backlogTargetsNow(s);
|
|
2952
|
-
|
|
2953
|
-
// 同じ s.url(同期ミラーが1つでもあるか)で、フォームを出さない条件と揃える
|
|
2954
|
-
var editable = !!(s && s.url);
|
|
3123
|
+
var editable = backlogTargetsEditable();
|
|
2955
3124
|
|
|
2956
3125
|
var listEl = el("div", { class: "ds-target-list" });
|
|
2957
3126
|
var current = el("div", { class: "ds-current" }, [
|
|
@@ -3151,12 +3320,13 @@ ${capabilityState.toString()}
|
|
|
3151
3320
|
// 伝えてフォームを出さない形なので、課題管理だけ不揃いだった。
|
|
3152
3321
|
// 一覧そのもの(現在の接続先)は**そういう案件にも出す**——読めているものを隠さない
|
|
3153
3322
|
//
|
|
3154
|
-
//
|
|
3155
|
-
//
|
|
3156
|
-
//
|
|
3323
|
+
// **判定は「同期したことがあるか」ではなく「この画面から投入できる案件か」(#138)。**
|
|
3324
|
+
// 接続先の正本(issues-config.json)は雛形から作られるので、立ち上げ直後の案件にも
|
|
3325
|
+
// 実在する。同期ミラーの有無で門前払いすると、**新規案件がこの画面から
|
|
3326
|
+
// Backlog に繋げない**——設定UIを置いている意味が無くなる
|
|
3157
3327
|
box.appendChild(backlogTargetsSection(s));
|
|
3158
|
-
if (!
|
|
3159
|
-
box.appendChild(el("p", { class: "sec-how", text: "
|
|
3328
|
+
if (!backlogTargetsEditable()) {
|
|
3329
|
+
box.appendChild(el("p", { class: "sec-how", text: "この案件の Backlog は別の経路で同期しているため、この画面からは接続先を変更できません。変更が必要なときは cortex の担当者にご連絡ください。" }));
|
|
3160
3330
|
used = true;
|
|
3161
3331
|
}
|
|
3162
3332
|
// **接続先の下に置く。** リアルタイム同期は「どのプロジェクトを見るか」が
|
|
@@ -3476,7 +3646,11 @@ ${capabilityState.toString()}
|
|
|
3476
3646
|
// 別名を付けたいときだけ入れてもらう。
|
|
3477
3647
|
namePlaceholder: "表示名(省略するとSlackから取得します)",
|
|
3478
3648
|
requiredNote: "チャンネルのURLを入力してください。",
|
|
3479
|
-
|
|
3649
|
+
// **招待の話を先頭に置く。** Slack の仕様で、ボットは**公開チャンネルでも**招待されるまで
|
|
3650
|
+
// 中身を1件も読めない(conversations.history が not_in_channel を返す・2026-08-16 実測+公式ドキュメント)。
|
|
3651
|
+
// 以前は「名前が取得できない」を主に書いていたが、読めない方が重い話なので順序を入れ替えた。
|
|
3652
|
+
hint: "先に Slack でそのチャンネルを開き、下のコマンドを貼って Cortex を招待してください。公開チャンネルでも招待が必要で、招待していないと中身を読めません。そのあと、チャンネル名を右クリック →「リンクをコピー」で出るURLを下に貼ります。追加しただけでは Gold 昇格には載りません(載せるには各チャンネルの「Gold昇格を始める」を押します)。招待前に登録すると名前は「名前未取得」になり、招待後にGold昇格をONにすると実名に変わります。",
|
|
3653
|
+
copyText: "/invite @Cortex",
|
|
3480
3654
|
// **「Goldに載る」と書かない。** 追加だけでは載らないので嘘になる。
|
|
3481
3655
|
// 確認する理由は「中央Botで読みに行く」「他案件の取り違え」。
|
|
3482
3656
|
confirmLines: function (v) {
|
|
@@ -3487,6 +3661,10 @@ ${capabilityState.toString()}
|
|
|
3487
3661
|
"",
|
|
3488
3662
|
v,
|
|
3489
3663
|
"",
|
|
3664
|
+
// Slack の仕様(公開チャンネルでも招待が要る)を、最後にもう一度置く。
|
|
3665
|
+
// 「追加したのに何も取り込まれない」の原因のほとんどがこれ。
|
|
3666
|
+
"※ Slack でこのチャンネルに Cortex を招待していないと、中身は読めません。",
|
|
3667
|
+
"",
|
|
3490
3668
|
"追加しますか?",
|
|
3491
3669
|
];
|
|
3492
3670
|
},
|
|
@@ -3758,6 +3936,9 @@ ${capabilityState.toString()}
|
|
|
3758
3936
|
var box = el("div", opts.adder ? { class: "ds-addform", "data-adder": opts.adder } : { class: "ds-addform" }, [
|
|
3759
3937
|
el("div", { class: "ds-addform-title", text: opts.title }),
|
|
3760
3938
|
opts.hint ? el("p", { class: "sec-how", text: opts.hint }) : null,
|
|
3939
|
+
// 手順の中に「どこかへ貼る文字列」があるときは、**手で打たせない**。
|
|
3940
|
+
// 「/invite @Cortex」のような短い文字列でも、打ち間違えると黙って失敗する
|
|
3941
|
+
opts.copyText ? copyRow(opts.copyText) : null,
|
|
3761
3942
|
main,
|
|
3762
3943
|
name,
|
|
3763
3944
|
wsSelect,
|
|
@@ -4344,7 +4525,16 @@ ${capabilityState.toString()}
|
|
|
4344
4525
|
icon: "🐙",
|
|
4345
4526
|
title: "GitHub の読み取りトークン",
|
|
4346
4527
|
desc: "開発リポジトリの Issues / Discussions を読み、Gold の原料にするために使います。",
|
|
4347
|
-
|
|
4528
|
+
// **第一の案内は GitHub App(#138)。** 実装の本線は App のインストールトークンで、
|
|
4529
|
+
// 個人トークンは App を入れられない組織のフォールバックでしかない(既存案件は
|
|
4530
|
+
// すでに App で読めている)。画面が個人トークンを勧め続けると、発行と入れ直しが
|
|
4531
|
+
// 要る側に人が流れ続け、**入れ直しの要らない仕組みへ移れない**。
|
|
4532
|
+
// 順序を入れ替えるだけで、どちらも残す——App を入れられない組織は実在する。
|
|
4533
|
+
appFirst: {
|
|
4534
|
+
text: "まず、読み取りたいリポジトリのある組織に Cortex の GitHub App を入れてください。トークンの発行も、期限が来たときの入れ直しも要りません。",
|
|
4535
|
+
linkText: "組織に GitHub App をインストール →",
|
|
4536
|
+
},
|
|
4537
|
+
how: "App を入れられない組織では、代わりにトークンを使います。GitHub → Settings → Developer settings → Personal access tokens で、対象リポジトリに Contents / Issues の Read-only を付けて発行",
|
|
4348
4538
|
},
|
|
4349
4539
|
};
|
|
4350
4540
|
|
|
@@ -4358,6 +4548,15 @@ ${capabilityState.toString()}
|
|
|
4358
4548
|
* カードは #101 で能力モーダルの中へ移した(buildDataSources の pushCap 経由)。
|
|
4359
4549
|
* まとめて並べる renderSecrets() は呼び出し元が無くなったので消してある。
|
|
4360
4550
|
*/
|
|
4551
|
+
function secretPayload(intake, kind, value) {
|
|
4552
|
+
var body = { projectKey: intake.projectKey, kind: kind, value: value };
|
|
4553
|
+
if (kind === "backlog") {
|
|
4554
|
+
var space = backlogSecretSpace();
|
|
4555
|
+
if (space) body.space = space;
|
|
4556
|
+
}
|
|
4557
|
+
return body;
|
|
4558
|
+
}
|
|
4559
|
+
|
|
4361
4560
|
function secretCard(kind, meta) {
|
|
4362
4561
|
// **いま何で動いているか**を先に出す。入れ直したのに反映されていない状態が
|
|
4363
4562
|
// 画面で分かるようにする(接続状況は夜間バッチのスナップショットなので、
|
|
@@ -4389,7 +4588,11 @@ ${capabilityState.toString()}
|
|
|
4389
4588
|
method: "POST",
|
|
4390
4589
|
headers: { "content-type": "application/json" },
|
|
4391
4590
|
// **シークレット名は送らない。** どの連携かだけを送り、書き込み先はサーバが案件から決める。
|
|
4392
|
-
|
|
4591
|
+
// **Backlog はスペースだけ名乗る(#138)。** 名前を組み立てるのはサーバで、
|
|
4592
|
+
// こちらが渡すのは「どのスペースの鍵か」という材料だけ。
|
|
4593
|
+
// **押した時点で読む。** 組み立て時に固めると、接続先を登録した直後に
|
|
4594
|
+
// 貼った鍵が「スペースを特定できません」で断られる
|
|
4595
|
+
body: JSON.stringify(secretPayload(intake, kind, value)),
|
|
4393
4596
|
});
|
|
4394
4597
|
var j = await res.json().catch(function () { return {}; });
|
|
4395
4598
|
if (!res.ok) {
|
|
@@ -4426,6 +4629,15 @@ ${capabilityState.toString()}
|
|
|
4426
4629
|
// **未設定のときだけ、先に試すべき手を出す。** 既に動いているカードに
|
|
4427
4630
|
// 「まずこれをやれ」と出すと、直す必要が無いものを触らせることになる
|
|
4428
4631
|
!src && meta.selfServe ? el("p", { class: "sec-preferred", text: meta.selfServe.text }) : null,
|
|
4632
|
+
// **本線(App)はトークンの手順より先に、いつでも出す(#138)。**
|
|
4633
|
+
// トークンで動いている案件こそ App へ移してほしいので、selfServe と違って
|
|
4634
|
+
// 「未設定のときだけ」にはしない
|
|
4635
|
+
meta.appFirst
|
|
4636
|
+
? el("p", { class: "sec-preferred" }, [
|
|
4637
|
+
document.createTextNode(meta.appFirst.text + " "),
|
|
4638
|
+
el("a", { href: GITHUB_APP_INSTALL_URL, target: "_blank", rel: "noopener", text: meta.appFirst.linkText }),
|
|
4639
|
+
])
|
|
4640
|
+
: null,
|
|
4429
4641
|
el("p", { class: "sec-how", text: meta.how }),
|
|
4430
4642
|
meta.selfServe ? el("p", { class: "sec-how", text: meta.selfServe.note }) : null,
|
|
4431
4643
|
input,
|
package/package.json
CHANGED