@takagaki/cortex-decisions-viewer 0.4.81 → 0.4.83
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 +356 -29
- package/package.json +1 -1
package/dist/render.js
CHANGED
|
@@ -1253,7 +1253,7 @@ a.ds-name:hover { text-decoration: underline; }
|
|
|
1253
1253
|
.ds-check-label, .ds-check-action, .ds-check-detail,
|
|
1254
1254
|
.ds-pipe-label, .ds-pipe-todo, .ds-owner-state,
|
|
1255
1255
|
.ds-empty, .sec-how, .modal-desc, .ds-toggle-msg,
|
|
1256
|
-
.ds-drive-note, .ds-inactive-note, .ds-legacy-chip, .ds-keys-code {
|
|
1256
|
+
.ds-drive-note, .ds-inactive-note, .ds-legacy-chip, .ds-keys-code, .ds-gate-help {
|
|
1257
1257
|
min-width: 0; white-space: normal; overflow-wrap: anywhere;
|
|
1258
1258
|
}
|
|
1259
1259
|
.ds-toggle { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 6px; }
|
|
@@ -1290,6 +1290,23 @@ a.ds-name:hover { text-decoration: underline; }
|
|
|
1290
1290
|
.ds-gate-ok { color: #1f7a45; }
|
|
1291
1291
|
.ds-gate-warn { color: #b45309; }
|
|
1292
1292
|
.ds-gate-muted { color: #6b7280; }
|
|
1293
|
+
/* 行の状態に添える「次に誰が何をするか」(2026-08-18)。**必ず自分の行を占める**
|
|
1294
|
+
(flex: 1 1 100%)——状態バッジの横に押し込むと、いちばん読ませたい一文が
|
|
1295
|
+
カード幅に負けて省略される。状態の語より弱く、しかし読める大きさで出す */
|
|
1296
|
+
.ds-gate-help { flex: 1 1 100%; margin: 4px 0 0; font-size: 12px; line-height: 1.7; color: #6b7280; }
|
|
1297
|
+
/* 「接続を確認」を押した結果(2026-08-19)。**必ず自分の行を占める**(flex: 1 1 100%)。
|
|
1298
|
+
夜間のスナップショットの状態バッジと**同時に2つの判定を並べない**ため、押したあとは
|
|
1299
|
+
状態バッジをこの中の判定へ置き換える(同じ画面で「⚠️」と「接続OK」が並ぶのが、
|
|
1300
|
+
この節で3回直してきた壊れ方そのもの) */
|
|
1301
|
+
.ds-probe { flex: 1 1 100%; margin-top: 8px; padding-top: 8px; border-top: 1px dashed var(--border); }
|
|
1302
|
+
.ds-probe-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
|
|
1303
|
+
/* いつ測った結果かを必ず添える。夜間のスナップショットと混ざると、どちらの話か読めない */
|
|
1304
|
+
.ds-probe-at { font-size: 11.5px; color: var(--muted); }
|
|
1305
|
+
.ds-probe-help { margin: 6px 0 0; font-size: 12px; line-height: 1.7; color: #6b7280;
|
|
1306
|
+
min-width: 0; white-space: normal; overflow-wrap: anywhere; }
|
|
1307
|
+
/* Slack が名指しした不足スコープ。**そのまま読める形で出す**(言い換えると検索できない) */
|
|
1308
|
+
.ds-probe-scope { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; background: #fef3c7;
|
|
1309
|
+
border: 1px solid #fde68a; border-radius: 6px; padding: 1px 6px; user-select: all; }
|
|
1293
1310
|
/* 意図的にOFFにした能力(#125②)。**グレーにして誘わない**——未接続(入口を出してよい)
|
|
1294
1311
|
とは別物で、同じ色にすると「押して繋ぎましょう」と読まれる */
|
|
1295
1312
|
.ds-gate-off { color: #9ca3af; }
|
|
@@ -1322,6 +1339,9 @@ a.ds-name:hover { text-decoration: underline; }
|
|
|
1322
1339
|
.ds-addform .sec-input { margin-bottom: 6px; }
|
|
1323
1340
|
.sec-input-inline { width: auto; max-width: 220px; margin-bottom: 0; }
|
|
1324
1341
|
.sec-how { color: var(--muted); font-size: 12px; line-height: 1.7; margin: 0 0 10px; }
|
|
1342
|
+
/* 「この接続は何のためのものでないか」(2026-08-18)。**説明文より強く出す**——
|
|
1343
|
+
ここを読み飛ばした人が、社内チャンネルのためにボタンを押し、必ず失敗する */
|
|
1344
|
+
.ds-scope-note { color: var(--text); }
|
|
1325
1345
|
/* いまの接続先(設定を変える口の直前に置く。値は本文色で、ラベルだけ弱める) */
|
|
1326
1346
|
.ds-current { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; margin: 0 0 8px; font-size: 12.5px; }
|
|
1327
1347
|
.ds-current-label { color: var(--muted); flex: 0 0 auto; }
|
|
@@ -2015,10 +2035,79 @@ ${capabilityState.toString()}
|
|
|
2015
2035
|
// **状態だけでなく、次にすることを書く。** 「bot未招待」では何をすればよいか分からない
|
|
2016
2036
|
// (公開チャンネルでも招待が要ることは Slack の仕様で、知らない人のほうが多い)
|
|
2017
2037
|
"not_in_channel": { text: "Slackで招待してください", cls: "ds-gate-warn" },
|
|
2038
|
+
// **招待では直らない(2026-08-19 の不具合報告)。** 非公開チャンネルで groups:read が
|
|
2039
|
+
// 足りていたのに、実測がこれを「不明」に潰していたので画面には「接続OK」としか出ず、
|
|
2040
|
+
// 名前が引けない理由が一切分からなかった(原因の特定に半日かかった)。
|
|
2041
|
+
// **状態の語で「招待」と言わない**——言うと、何度招待しても直らないものを繰り返させる
|
|
2042
|
+
"missing_scope": { text: "権限が足りません", cls: "ds-gate-warn" },
|
|
2018
2043
|
"unreachable": { text: "アクセス不可", cls: "ds-gate-warn" },
|
|
2019
2044
|
"no_token": { text: "トークン未設定", cls: "ds-gate-muted" },
|
|
2020
2045
|
"unknown": { text: "不明", cls: "ds-gate-muted" },
|
|
2021
2046
|
};
|
|
2047
|
+
/* ---------------------------------------------------------------------------
|
|
2048
|
+
* no_token には**経路が2つある**(2026-08-18 の不具合報告)
|
|
2049
|
+
*
|
|
2050
|
+
* Slack のチャンネルは、どのワークスペースにあるかで読み取りの鍵が別物になる:
|
|
2051
|
+
* - workspace 省略 … クラスメソッドの社内ワークスペース。中央 Bot の鍵(案件リポジトリの
|
|
2052
|
+
* 設定)で読む。**画面から設定できるものが1つも無い。**
|
|
2053
|
+
* - workspace: "T…" … お客様のワークスペース。下の「お客様の Slack ワークスペースを接続」で
|
|
2054
|
+
* 承認を貰って読む。**画面から自己解決できる。**
|
|
2055
|
+
*
|
|
2056
|
+
* どちらも no_token に潰して「トークン未設定」と出していたため、社内チャンネルを
|
|
2057
|
+
* 登録した人が「そのトークンをどこで設定するのか」を探し、直下にある唯一それらしい
|
|
2058
|
+
* ボタン(顧客ワークスペースの接続)へ吸い込まれた。押すと社内ワークスペースの承認画面が
|
|
2059
|
+
* 出て失敗し、「もう一度お試しください」と言われてまた押す——**何度試しても直らない**まま
|
|
2060
|
+
* 不具合報告になった。状態だけでなく、**この行で次に誰が何をするのか**を出し分ける。
|
|
2061
|
+
*
|
|
2062
|
+
* **社内側で「担当者にご連絡ください」と書くのは後退ではない。** 画面から設定できる
|
|
2063
|
+
* ものが本当に無いので、自己解決できると誤認させて空振りを繰り返させる方が損害が大きい。
|
|
2064
|
+
* ------------------------------------------------------------------------- */
|
|
2065
|
+
var DS_GATE_NO_TOKEN = {
|
|
2066
|
+
// 社内ワークスペース。**顧客ワークスペースの接続へ誘導しない**(押しても必ず失敗する)
|
|
2067
|
+
internal: {
|
|
2068
|
+
text: "管理者の設定が必要です",
|
|
2069
|
+
cls: "ds-gate-muted",
|
|
2070
|
+
help: "このチャンネルはクラスメソッドの社内ワークスペースにあります。読み取りを始めるには cortex 側(管理者)の設定が必要で、下の「お客様の Slack ワークスペースを接続」では設定できません。cortex の担当者にご連絡ください。",
|
|
2071
|
+
},
|
|
2072
|
+
// お客様のワークスペース。従来どおり接続へ誘導する(自己解決できる唯一の経路)
|
|
2073
|
+
customer: {
|
|
2074
|
+
text: "トークン未設定",
|
|
2075
|
+
cls: "ds-gate-muted",
|
|
2076
|
+
help: "このチャンネルがあるお客様の Slack ワークスペースが、まだ接続されていない(または接続が切れている)状態です。下の「お客様の Slack ワークスペースを接続」から接続してください。",
|
|
2077
|
+
},
|
|
2078
|
+
};
|
|
2079
|
+
/**
|
|
2080
|
+
* Slack のチャンネルが社内ワークスペースのものか。**workspace の有無だけで決まる**
|
|
2081
|
+
* (省略=社内。#104 でこの値が画面まで届いていることは slack-workspace.test.mjs が見張る)。
|
|
2082
|
+
*/
|
|
2083
|
+
function isInternalSlack(s) {
|
|
2084
|
+
return s.type === "slack" && !s.workspace;
|
|
2085
|
+
}
|
|
2086
|
+
/** 行に出す接続状態。no_token だけは経路(社内/顧客)で文面が分かれる */
|
|
2087
|
+
function dsGateOf(s) {
|
|
2088
|
+
if (s.gate === "no_token" && s.type === "slack") {
|
|
2089
|
+
return isInternalSlack(s) ? DS_GATE_NO_TOKEN.internal : DS_GATE_NO_TOKEN.customer;
|
|
2090
|
+
}
|
|
2091
|
+
return DS_GATE[s.gate] || DS_GATE["unknown"];
|
|
2092
|
+
}
|
|
2093
|
+
/**
|
|
2094
|
+
* 一覧・タイルに出す「⚠️ …」の要約。**呼び出しは2箇所(グループの summary と能力タイル)。**
|
|
2095
|
+
* 写経すると片方だけ古くなり、同じ画面で語が割れる(この節では実際に起きた)。
|
|
2096
|
+
*
|
|
2097
|
+
* **社内チャンネルだけが読めていないときは「トークン」と言わない。** 探しても設定する
|
|
2098
|
+
* 場所が画面に無いので、探した人が顧客ワークスペースの接続へ吸い込まれる(DS_GATE_NO_TOKEN
|
|
2099
|
+
* の項の事故がここから始まっている)。1件でも顧客側が混ざるときは従来の文面のまま——
|
|
2100
|
+
* そちらは接続で直せるので、経路を名指しできない要約に倒す方が正しい。
|
|
2101
|
+
*/
|
|
2102
|
+
function attentionSummary(items) {
|
|
2103
|
+
var bad = items.filter(function (s) { return sourceNeedsAttention(s); });
|
|
2104
|
+
if (!bad.length) return "";
|
|
2105
|
+
var head = "⚠️ " + bad.length + "件";
|
|
2106
|
+
var noToken = bad.filter(function (s) { return s.gate === "no_token"; });
|
|
2107
|
+
if (noToken.length !== bad.length) return head + "に問題";
|
|
2108
|
+
var allInternal = noToken.every(function (s) { return isInternalSlack(s); });
|
|
2109
|
+
return head + "が読めていません(" + (allInternal ? "管理者の設定が必要です" : "トークン未設定") + ")";
|
|
2110
|
+
}
|
|
2022
2111
|
var DS_KIND_ICON = {
|
|
2023
2112
|
"課題管理": "📋",
|
|
2024
2113
|
"会議": "🗒️",
|
|
@@ -2413,24 +2502,16 @@ ${capabilityState.toString()}
|
|
|
2413
2502
|
g.items.forEach(function (s) { cards.appendChild(extSourceCard(s, meta)); });
|
|
2414
2503
|
if (g.items.length >= 4) {
|
|
2415
2504
|
// 折りたたみ(既定は閉)。summaryに件数と状態の要約を出す。
|
|
2416
|
-
//
|
|
2417
|
-
//
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
g.items
|
|
2421
|
-
if (!sourceNeedsAttention(s)) return;
|
|
2422
|
-
bad++;
|
|
2423
|
-
if (s.gate === "no_token") noToken++;
|
|
2424
|
-
});
|
|
2505
|
+
// **判定と文面は共有の attentionSummary を呼ぶ。** 以前はここに中身を写経して
|
|
2506
|
+
// いたが、片方だけ直すと同じ画面でマップが⚠️・直下が「接続OK」になる(実際に起きた)。
|
|
2507
|
+
// 原因が1つに集約できるときは原因を書く(「19件に問題」より、読んだ人が次に何を
|
|
2508
|
+
// すればよいか分かる)——その出し分けごと attentionSummary が持っている
|
|
2509
|
+
var warn = attentionSummary(g.items);
|
|
2425
2510
|
var sum = el("summary", {}, [
|
|
2426
2511
|
el("span", { text: meta.icon + " " + meta.label + "(" + g.items.length + (UNIT[g.type] || "件") + ")" }),
|
|
2427
|
-
|
|
2512
|
+
warn === ""
|
|
2428
2513
|
? el("span", { class: "badge ds-sum-ok", text: "接続OK" })
|
|
2429
|
-
|
|
2430
|
-
// 読んだ人が次に何をすればよいか分かる(実際、19件の原因がトークン1本ということがあった)。
|
|
2431
|
-
: el("span", { class: "badge ds-sum-warn", text: noToken === bad
|
|
2432
|
-
? "⚠️ " + bad + "件が読めていません(トークン未設定)"
|
|
2433
|
-
: "⚠️ " + bad + "件に問題" }),
|
|
2514
|
+
: el("span", { class: "badge ds-sum-warn", text: warn }),
|
|
2434
2515
|
]);
|
|
2435
2516
|
var det = el("details", { class: "ds-group" }, [sum, cards]);
|
|
2436
2517
|
pushCap(capOf(g.type), det);
|
|
@@ -2676,15 +2757,14 @@ ${capabilityState.toString()}
|
|
|
2676
2757
|
parts.push((CAP_STATE_MARK[st] || "") + (CAP_STATE_TEXT[derived] || "状態が判定できません"));
|
|
2677
2758
|
} else if (st === "warn") {
|
|
2678
2759
|
if (live.length) {
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
: "⚠️ " + bad + "件に問題");
|
|
2760
|
+
// **文面は共有の attentionSummary が持つ。** グループの summary と同じ文で、
|
|
2761
|
+
// 写経すると片方だけ古くなる(社内/顧客の出し分けもここに要る)。
|
|
2762
|
+
//
|
|
2763
|
+
// **数えて0件でも文を出す。** warn はパイプラインの失敗からも立つので
|
|
2764
|
+
// (ライブでない経路)、行の gate が全部 ok のまま warn になることがある。
|
|
2765
|
+
// 空にすると⚠️のタイルの2行目が消えるので、従来どおりの件数表現に倒す
|
|
2766
|
+
var warn = attentionSummary(live);
|
|
2767
|
+
parts.push(warn || "⚠️ 0件に問題");
|
|
2688
2768
|
} else {
|
|
2689
2769
|
// 内部能力の warn はパイプラインの失敗由来。数えるものが無いので件数を出さない
|
|
2690
2770
|
parts.push("⚠️ 同期が失敗しています");
|
|
@@ -2972,6 +3052,210 @@ ${capabilityState.toString()}
|
|
|
2972
3052
|
* 関わらず必ず出し、成功しても一覧からは消さずに行へ取り消し線を引く(一覧は夜間
|
|
2973
3053
|
* スナップショットなので、翌朝まで残るのが正しい状態)。
|
|
2974
3054
|
*/
|
|
3055
|
+
/* ---------------------------------------------------------------------------
|
|
3056
|
+
* 「接続を確認」(2026-08-19 の不具合報告3件)
|
|
3057
|
+
* ---------------------------------------------------------------------------
|
|
3058
|
+
* 3つの別々の原因(トークン未登録・groups:read 不足・スナップショットが古い)が、
|
|
3059
|
+
* **画面上はどれも区別できなかった。** 共通していたのは「画面を見ても次に何をすれば
|
|
3060
|
+
* いいか決まらない」こと。実測は毎晩1回しか走らないので、招待やスコープを直した人が
|
|
3061
|
+
* 直ったことを確かめる手段も無かった。**押した瞬間に測って、理由と次の一手を返す。**
|
|
3062
|
+
*
|
|
3063
|
+
* **判定はここで作らない。** 状態の語は DS_GATE、社内/顧客の出し分けは
|
|
3064
|
+
* DS_GATE_NO_TOKEN が持っていて、どちらも dsGateOf を通して取る。ここで文面を
|
|
3065
|
+
* 書き写すと、**同じ行の状態バッジと確認結果で語が割れる**(この節で3回直した形)。
|
|
3066
|
+
*
|
|
3067
|
+
* 他アダプタ(GitHub / Backlog / Figma / Drive)を足すときの拡張点:
|
|
3068
|
+
* - 判定と語彙は infra/lambda/shared/slack-gate.mjs 側に足す(画面は DS_GATE のまま)
|
|
3069
|
+
* - この節に増やすのは probeHelpNodes の「次の一手」だけ。**アダプタ固有の分岐を
|
|
3070
|
+
* probeVerdict に入れない**(入れた瞬間に判定が画面側にも生まれる)
|
|
3071
|
+
* - 行に出す条件(下の extSourceCard)を s.type === "slack" から広げる
|
|
3072
|
+
* ------------------------------------------------------------------------- */
|
|
3073
|
+
|
|
3074
|
+
/**
|
|
3075
|
+
* 確認結果の状態バッジ。**既存の dsGateOf をそのまま通す。**
|
|
3076
|
+
*
|
|
3077
|
+
* こうしておくと no_token の社内/顧客の出し分け(DS_GATE_NO_TOKEN)が確認結果にも
|
|
3078
|
+
* そのまま効く。写経すると、同じ画面で行は「管理者の設定が必要です」・確認結果は
|
|
3079
|
+
* 「トークン未設定」になり、社内チャンネルを登録した人が**また**顧客ワークスペースの
|
|
3080
|
+
* 接続へ吸い込まれる(2026-08-18 の不具合そのもの)。
|
|
3081
|
+
*/
|
|
3082
|
+
function probeVerdict(s, r) {
|
|
3083
|
+
return dsGateOf({ type: s.type, workspace: s.workspace, gate: r.gate });
|
|
3084
|
+
}
|
|
3085
|
+
|
|
3086
|
+
/**
|
|
3087
|
+
* 判定ごとの「次の一手」。**状態の語だけでは足りない**——今日の3件はどれも
|
|
3088
|
+
* 「何が起きているか」は分かっても「次に何をすればいいか」が決まらなかった。
|
|
3089
|
+
*/
|
|
3090
|
+
function probeHelpNodes(s, r) {
|
|
3091
|
+
var p = function (t) { return el("p", { class: "ds-probe-help", text: t }); };
|
|
3092
|
+
var out = [];
|
|
3093
|
+
if (r.gate === "ok") {
|
|
3094
|
+
// **表示名が引けたかまで言う。** 読めているのに名前が「(名前未取得: C0BQ…)」の
|
|
3095
|
+
// ままだった案件があり、読めていること自体は正しかったので誰も疑わなかった
|
|
3096
|
+
out.push(p(r.name
|
|
3097
|
+
? "読めています。Slack で確認できた表示名は「" + r.name + "」です。"
|
|
3098
|
+
: "読めています。ただし表示名は引けませんでした(Slack の応答: " +
|
|
3099
|
+
(r.error || "不明") + ")。一覧の名前は登録したときのままになります。"));
|
|
3100
|
+
return out;
|
|
3101
|
+
}
|
|
3102
|
+
if (r.gate === "not_in_channel") {
|
|
3103
|
+
out.push(p("このチャンネルに Cortex を招待してください。公開チャンネルでも招待が必要で、" +
|
|
3104
|
+
"招待していないと中身を1件も読めません。Slack でこのチャンネルを開き、次のコマンドを貼ります。" +
|
|
3105
|
+
"招待したら、もう一度「接続を確認」を押して直ったことを確かめてください。"));
|
|
3106
|
+
// **手で打たせない。** 短い文字列でも打ち間違いは黙って失敗し、
|
|
3107
|
+
// 利用者には「招待したのに読まれない」としか見えない(copyRow の直上コメント)
|
|
3108
|
+
out.push(copyRow("/invite @Cortex"));
|
|
3109
|
+
return out;
|
|
3110
|
+
}
|
|
3111
|
+
if (r.gate === "missing_scope") {
|
|
3112
|
+
// **足りないスコープ名をそのまま出す(2026-08-19 の不具合の本体)。**
|
|
3113
|
+
// Slack は needed で名指しで返しているのに、実測がそれを捨てて「不明」にしていた。
|
|
3114
|
+
// **招待と言わない**——招待では直らないので、言うと直らない操作を繰り返させる
|
|
3115
|
+
var line = el("p", { class: "ds-probe-help" });
|
|
3116
|
+
line.appendChild(document.createTextNode("Cortex の Slack App に権限(スコープ)が足りません。不足しているのは "));
|
|
3117
|
+
line.appendChild(el("code", { class: "ds-probe-scope", text: r.needed || "不明(Slack が名前を返しませんでした)" }));
|
|
3118
|
+
line.appendChild(document.createTextNode(" です。"));
|
|
3119
|
+
out.push(line);
|
|
3120
|
+
out.push(p("これは招待では直りません。Slack App にこのスコープを追加して再インストールする必要があるので、" +
|
|
3121
|
+
"cortex の担当者にこのスコープ名をそのまま伝えてください。" +
|
|
3122
|
+
(r.readable
|
|
3123
|
+
? "なお、チャンネルの中身そのものは読めています(毎晩の取り込みは動いています)。直らないのは表示名です。"
|
|
3124
|
+
: "")));
|
|
3125
|
+
return out;
|
|
3126
|
+
}
|
|
3127
|
+
if (r.gate === "no_token") {
|
|
3128
|
+
// **社内/顧客の出し分けは既存の1箇所から取る。** ここで書き分けると必ずずれる
|
|
3129
|
+
var help = probeVerdict(s, r).help;
|
|
3130
|
+
if (help) out.push(p(help));
|
|
3131
|
+
return out;
|
|
3132
|
+
}
|
|
3133
|
+
if (r.gate === "unreachable") {
|
|
3134
|
+
out.push(p("チャンネルが見つからないか、アーカイブされています。" +
|
|
3135
|
+
"Slack でこのチャンネルがまだ存在するかを確かめてください。" +
|
|
3136
|
+
"アーカイブしたチャンネルなら、この行の「登録を解除」で読み取り対象から外せます。" +
|
|
3137
|
+
"存在するのに見つからない場合は、Cortex が入っていない別ワークスペースのチャンネルの可能性があるので、" +
|
|
3138
|
+
"cortex の担当者にご連絡ください。"));
|
|
3139
|
+
return out;
|
|
3140
|
+
}
|
|
3141
|
+
// unknown。**生のエラーを隠さない**——「不明」だけだと切り分けが始められない
|
|
3142
|
+
out.push(p("読めるかどうかを確かめられませんでした。Slack の応答は「" + (r.error || "不明") + "」です。" +
|
|
3143
|
+
"少し時間をおいてもう一度お試しください。同じ応答が続く場合は、この文言をそのまま cortex の担当者にお伝えください。"));
|
|
3144
|
+
return out;
|
|
3145
|
+
}
|
|
3146
|
+
|
|
3147
|
+
/**
|
|
3148
|
+
* 表示名の取り直し。**確認とは別のボタンにしてある。**
|
|
3149
|
+
*
|
|
3150
|
+
* 確認(?op=probe)は読むだけなので誰でも押してよいと決めた(オーナー判断・2026-08-19)。
|
|
3151
|
+
* 名前の更新は案件リポジトリへのコミットなので、**設定変更と同じ入口(?op=config)**に
|
|
3152
|
+
* 通す(sendConfig)。確認のついでに黙って書き込む形にすると、「読むだけだから誰でも
|
|
3153
|
+
* 押してよい」という前提そのものが崩れる。**押す前に、何がどう変わるかを見せる。**
|
|
3154
|
+
*
|
|
3155
|
+
* 出すのは**違っているときだけ**。同じ名前のときにボタンだけ出すと、押しても
|
|
3156
|
+
* 「変更はありません」と返るだけの操作になる。
|
|
3157
|
+
*/
|
|
3158
|
+
function renameChannelRow(s, r, card) {
|
|
3159
|
+
if (!r.name || r.name === s.name) return null;
|
|
3160
|
+
var box = el("div");
|
|
3161
|
+
box.appendChild(el("p", { class: "ds-probe-help",
|
|
3162
|
+
text: "一覧の表示名「" + (s.name || "(未設定)") + "」は、Slack のいまの名前「" + r.name + "」と違います。" +
|
|
3163
|
+
"更新すると、この案件の設定ファイル(チャット/channels.json)の表示名だけが書き換わります。" +
|
|
3164
|
+
"読み取りの対象・範囲は変わりません。" }));
|
|
3165
|
+
var row = el("div", { class: "ds-toggle" });
|
|
3166
|
+
var btn = el("button", { class: "ds-toggle-btn", type: "button", text: "表示名を更新する" });
|
|
3167
|
+
var msg = el("div", { class: "ds-toggle-msg" });
|
|
3168
|
+
btn.addEventListener("click", function () {
|
|
3169
|
+
// **名前は送らない。** サーバが Slack から引き直したものだけを書く(引けなければ
|
|
3170
|
+
// 断って既存の名前を残す)。画面から任意の名前を送れる形にすると、
|
|
3171
|
+
// 名前の取り直しの顔をした自由な改名になる
|
|
3172
|
+
var payload = { op: "refresh-channel-name", ref: s.ref };
|
|
3173
|
+
if (s.workspace) payload.workspace = s.workspace;
|
|
3174
|
+
sendConfig(payload, msg, btn, function () {
|
|
3175
|
+
// 一覧は夜間のスナップショットなので、その場では変わらない。行だけ追いつかせる
|
|
3176
|
+
var nameEl = card.querySelector(".ds-name");
|
|
3177
|
+
if (nameEl) nameEl.textContent = r.name;
|
|
3178
|
+
btn.disabled = true;
|
|
3179
|
+
});
|
|
3180
|
+
});
|
|
3181
|
+
row.appendChild(btn);
|
|
3182
|
+
row.appendChild(msg);
|
|
3183
|
+
box.appendChild(row);
|
|
3184
|
+
return box;
|
|
3185
|
+
}
|
|
3186
|
+
|
|
3187
|
+
/**
|
|
3188
|
+
* 「接続を確認」の一式(ボタン+結果)。
|
|
3189
|
+
*
|
|
3190
|
+
* **押したら、その行の状態バッジを置き換える。** 夜間のスナップショットの判定と
|
|
3191
|
+
* いま測った判定を**同じ行に並べない**ため——並べると「⚠️ と 接続OK が同時に出ている」
|
|
3192
|
+
* になり、この節で3回直してきた食い違いを画面の側から作ることになる。
|
|
3193
|
+
* 置き換えたことが分かるよう、いつ測ったかを必ず添える。
|
|
3194
|
+
*
|
|
3195
|
+
* **結果は保存しない。** fleet-status.json は夜間のスナップショットのまま置く
|
|
3196
|
+
* (押すたびにコミットが増えるのを避ける)。表示名の取り直しだけが例外で、
|
|
3197
|
+
* そちらは正本(channels.json)を直す価値があるので別のボタンで書き込む。
|
|
3198
|
+
*/
|
|
3199
|
+
function probeSection(s, card, gateEl) {
|
|
3200
|
+
var wrap = el("div", { class: "ds-probe" });
|
|
3201
|
+
var head = el("div", { class: "ds-probe-head" });
|
|
3202
|
+
var btn = el("button", { class: "ds-toggle-btn", type: "button", text: "接続を確認" });
|
|
3203
|
+
var msg = el("span", { class: "ds-toggle-msg" });
|
|
3204
|
+
var at = el("span", { class: "ds-probe-at" });
|
|
3205
|
+
var body = el("div");
|
|
3206
|
+
head.appendChild(btn);
|
|
3207
|
+
head.appendChild(msg);
|
|
3208
|
+
head.appendChild(at);
|
|
3209
|
+
wrap.appendChild(head);
|
|
3210
|
+
wrap.appendChild(body);
|
|
3211
|
+
|
|
3212
|
+
btn.addEventListener("click", async function () {
|
|
3213
|
+
var intake = SITE.intake;
|
|
3214
|
+
if (!intake) return;
|
|
3215
|
+
btn.disabled = true;
|
|
3216
|
+
msg.className = "ds-toggle-msg";
|
|
3217
|
+
msg.textContent = "確認中…";
|
|
3218
|
+
at.textContent = "";
|
|
3219
|
+
body.textContent = "";
|
|
3220
|
+
var payload = { type: "slack", ref: s.ref };
|
|
3221
|
+
if (s.workspace) payload.workspace = s.workspace;
|
|
3222
|
+
var pfx = intake.url + (intake.url.indexOf("?") === -1 ? "?" : "&");
|
|
3223
|
+
try {
|
|
3224
|
+
var res = await fetch(pfx + "op=probe&t=" + encodeURIComponent(intake.token), {
|
|
3225
|
+
method: "POST",
|
|
3226
|
+
headers: { "content-type": "application/json" },
|
|
3227
|
+
body: JSON.stringify(payload),
|
|
3228
|
+
});
|
|
3229
|
+
var j = await res.json().catch(function () { return {}; });
|
|
3230
|
+
if (!res.ok) {
|
|
3231
|
+
// サーバの文言をそのまま出す(回数制限の案内・未対応のアダプタの説明を含んでいる)
|
|
3232
|
+
msg.className = "ds-toggle-msg is-error";
|
|
3233
|
+
msg.textContent = "✗ " + (j.message || "確認できませんでした。");
|
|
3234
|
+
btn.disabled = false;
|
|
3235
|
+
return;
|
|
3236
|
+
}
|
|
3237
|
+
msg.textContent = "";
|
|
3238
|
+
var v = probeVerdict(s, j);
|
|
3239
|
+
gateEl.className = "ds-gate " + v.cls;
|
|
3240
|
+
gateEl.textContent = v.text;
|
|
3241
|
+
at.textContent = "いま測った結果(" + (dsFmtTime(j.checkedAt) || "たった今") + ")";
|
|
3242
|
+
probeHelpNodes(s, j).forEach(function (n) { body.appendChild(n); });
|
|
3243
|
+
// **登録ファイルが channels.json の行にだけ出す。** 書き換える先がそこなので、
|
|
3244
|
+
// dev-sources.json だけに載っている行(origin: explicit)では必ず 404 になる
|
|
3245
|
+
// ——「登録を解除」が同じ理由で同じ条件を持っている
|
|
3246
|
+
if (s.origin !== "explicit") {
|
|
3247
|
+
var rename = renameChannelRow(s, j, card);
|
|
3248
|
+
if (rename) body.appendChild(rename);
|
|
3249
|
+
}
|
|
3250
|
+
} catch (e) {
|
|
3251
|
+
msg.className = "ds-toggle-msg is-error";
|
|
3252
|
+
msg.textContent = "✗ 通信に失敗しました。";
|
|
3253
|
+
}
|
|
3254
|
+
btn.disabled = false;
|
|
3255
|
+
});
|
|
3256
|
+
return wrap;
|
|
3257
|
+
}
|
|
3258
|
+
|
|
2975
3259
|
function removeChannelButton(s, card) {
|
|
2976
3260
|
return removeButton({
|
|
2977
3261
|
label: "登録を解除",
|
|
@@ -3517,7 +3801,7 @@ ${capabilityState.toString()}
|
|
|
3517
3801
|
if (!list) return null;
|
|
3518
3802
|
|
|
3519
3803
|
var box = el("div", { class: "ds-addform", "data-adder": "slack-workspace" }, [
|
|
3520
|
-
el("div", { class: "ds-addform-title", text: "Slack ワークスペースを接続" }),
|
|
3804
|
+
el("div", { class: "ds-addform-title", text: "お客様の Slack ワークスペースを接続" }),
|
|
3521
3805
|
el("p", {
|
|
3522
3806
|
class: "sec-how",
|
|
3523
3807
|
text:
|
|
@@ -3526,6 +3810,22 @@ ${capabilityState.toString()}
|
|
|
3526
3810
|
"お願いする権限は読み取りだけで、Cortex から投稿することはありません。" +
|
|
3527
3811
|
"接続したあと、読み取りたいチャンネルにボットを招待し、この画面から追加してください。",
|
|
3528
3812
|
}),
|
|
3813
|
+
// **「これは何のためのものでないか」を書く(2026-08-18 の不具合報告)。**
|
|
3814
|
+
// 社内ワークスペースのチャンネルが読めていない人が、設定する場所を探して
|
|
3815
|
+
// ここへ来る。押すと社内ワークスペースの承認画面が出て必ず失敗し、
|
|
3816
|
+
// 「もう一度お試しください」に従ってまた押す——何度試しても直らない。
|
|
3817
|
+
//
|
|
3818
|
+
// **箱そのものは消さない。** 直上の renderExternalAdders のコメントが警告している
|
|
3819
|
+
// のと同じ失敗で、「顧客チャンネルがある案件だけ出す」にすると
|
|
3820
|
+
// **新規案件(0件)=一番繋ぎたい場面**でこの入口が消える。出したまま、
|
|
3821
|
+
// 使う場面と使わない場面をここで言い切る
|
|
3822
|
+
el("p", {
|
|
3823
|
+
class: "sec-how ds-scope-note",
|
|
3824
|
+
text:
|
|
3825
|
+
"クラスメソッドの社内ワークスペース(Classmethod Group)のチャンネルには、この接続は使いません。" +
|
|
3826
|
+
"社内のチャンネルは Cortex の中央ボットで読んでいるので、ここで接続しても読めるようにはなりません。" +
|
|
3827
|
+
"社内のチャンネルが読めていない場合は、cortex の担当者にご連絡ください。",
|
|
3828
|
+
}),
|
|
3529
3829
|
]);
|
|
3530
3830
|
|
|
3531
3831
|
// 接続済みの一覧。**0件でも「まだありません」と書く**——空白だと、
|
|
@@ -3567,7 +3867,10 @@ ${capabilityState.toString()}
|
|
|
3567
3867
|
}
|
|
3568
3868
|
box.appendChild(listBox);
|
|
3569
3869
|
|
|
3570
|
-
|
|
3870
|
+
// **ボタン自身に「お客様の」と書く(2026-08-18 の不具合報告)。** 社内チャンネルが
|
|
3871
|
+
// 読めていない人が押したのはこのボタンで、当時の文言は「Slack ワークスペースを接続」
|
|
3872
|
+
// ——誰のワークスペースの話かが書かれておらず、唯一それらしいボタンに見えた
|
|
3873
|
+
var btn = el("button", { class: "ds-toggle-btn", type: "button", text: "お客様の Slack ワークスペースを接続" });
|
|
3571
3874
|
var msg = el("div", { class: "ds-toggle-msg" });
|
|
3572
3875
|
btn.addEventListener("click", async function () {
|
|
3573
3876
|
var intake = SITE.intake;
|
|
@@ -4016,7 +4319,10 @@ ${capabilityState.toString()}
|
|
|
4016
4319
|
title: ORIGIN_HELP[s.origin],
|
|
4017
4320
|
}));
|
|
4018
4321
|
}
|
|
4019
|
-
var gate =
|
|
4322
|
+
var gate = dsGateOf(s);
|
|
4323
|
+
// **「接続を確認」が押されたら、この要素を live の判定で置き換える**(probeSection)。
|
|
4324
|
+
// 2つの判定を同じ行に並べないため、参照を持っておく
|
|
4325
|
+
var gateEl = el("span", { class: "ds-gate " + gate.cls, text: gate.text });
|
|
4020
4326
|
var card = el("div", { class: "ds-card" }, [
|
|
4021
4327
|
el("div", { class: "ds-card-main" }, [
|
|
4022
4328
|
el("span", { class: "ds-icon", text: meta.icon }),
|
|
@@ -4024,7 +4330,7 @@ ${capabilityState.toString()}
|
|
|
4024
4330
|
dsNameEl(s.name || "", s.url),
|
|
4025
4331
|
]),
|
|
4026
4332
|
badges,
|
|
4027
|
-
|
|
4333
|
+
gateEl,
|
|
4028
4334
|
]);
|
|
4029
4335
|
// チャンネルの登録解除。書き込み経路(intake Lambda)がある案件でだけ出す。
|
|
4030
4336
|
// **Slack だけ。** github は登録ファイルが別(dev-sources.json)で、下のトグルと
|
|
@@ -4082,6 +4388,27 @@ ${capabilityState.toString()}
|
|
|
4082
4388
|
}));
|
|
4083
4389
|
}
|
|
4084
4390
|
}
|
|
4391
|
+
// **「次に誰が何をするか」を、その行に書く(2026-08-18 の不具合報告)。**
|
|
4392
|
+
// 状態(トークン未設定)だけを出していたので、設定する場所を探した人が直下の
|
|
4393
|
+
// 顧客ワークスペースの接続へ吸い込まれた——社内チャンネルではそれが必ず失敗する。
|
|
4394
|
+
// 文面は DS_GATE_NO_TOKEN が経路ごとに持つ(社内は連絡先・顧客は接続の導線)。
|
|
4395
|
+
//
|
|
4396
|
+
// **ボタンより後ろに置く。** カードは横並びの flex なので、間に挟むと
|
|
4397
|
+
// 「登録を解除」が案内文の下の行へ落ちて、既存案件の行の形が変わる
|
|
4398
|
+
if (gate.help) card.appendChild(el("p", { class: "ds-gate-help", text: gate.help }));
|
|
4399
|
+
// 「接続を確認」(2026-08-19)。**押せる人を絞らない**——読むだけで設定を変えないので、
|
|
4400
|
+
// 気づいた人がその場で確かめられるほうがよい(オーナー判断)。書き込みになる
|
|
4401
|
+
// 「表示名を更新する」は、押した結果の中から別のボタンとして出る(probeSection)。
|
|
4402
|
+
//
|
|
4403
|
+
// **いまは Slack だけ。** GitHub は夜間の実測(probeGithubGate)が既にあるので繋ぐだけ、
|
|
4404
|
+
// Backlog / Figma / Drive は実測そのものが新規になる。同じ型で足せるよう、
|
|
4405
|
+
// 判定は infra/lambda/shared/slack-gate.mjs 側に置いてある(末尾に拡張点の説明がある)。
|
|
4406
|
+
//
|
|
4407
|
+
// **origin === "explicit" でも出す。** 確認は読むだけで、どの登録ファイル由来かに
|
|
4408
|
+
// 関係なく効く(書き込みになる「表示名を更新する」だけが由来で出し分かれる)
|
|
4409
|
+
if (SITE.intake && s.type === "slack" && s.ref) {
|
|
4410
|
+
card.appendChild(probeSection(s, card, gateEl));
|
|
4411
|
+
}
|
|
4085
4412
|
return card;
|
|
4086
4413
|
}
|
|
4087
4414
|
|
package/package.json
CHANGED