@takagaki/cortex-decisions-viewer 0.10.7 → 0.11.0
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 +525 -52
- package/package.json +1 -1
package/dist/render.js
CHANGED
|
@@ -19,6 +19,13 @@ exports.registeredRowsToAdd = registeredRowsToAdd;
|
|
|
19
19
|
exports.subtractRemovedRegistered = subtractRemovedRegistered;
|
|
20
20
|
exports.mergeRegisteredFleet = mergeRegisteredFleet;
|
|
21
21
|
exports.nextStateDelay = nextStateDelay;
|
|
22
|
+
exports.sanitizeSecretsNow = sanitizeSecretsNow;
|
|
23
|
+
exports.secretStateFromNow = secretStateFromNow;
|
|
24
|
+
exports.secretRegisteredAnywhere = secretRegisteredAnywhere;
|
|
25
|
+
exports.sanitizeCapabilitiesNow = sanitizeCapabilitiesNow;
|
|
26
|
+
exports.sanitizeGithubOwnersNow = sanitizeGithubOwnersNow;
|
|
27
|
+
exports.splitNeededParts = splitNeededParts;
|
|
28
|
+
exports.ingestedAtOf = ingestedAtOf;
|
|
22
29
|
exports.renderConnectionMap = renderConnectionMap;
|
|
23
30
|
/** SiteDataを単一の自己完結HTML(埋め込みデータ+CSS+JS)にレンダリングする */
|
|
24
31
|
function renderSite(site) {
|
|
@@ -1058,6 +1065,232 @@ function nextStateDelay(count, since, now) {
|
|
|
1058
1065
|
return null;
|
|
1059
1066
|
return left < UNSETTLED_POLL_MS ? left : UNSETTLED_POLL_MS;
|
|
1060
1067
|
}
|
|
1068
|
+
// ---------------------------------------------------------------------------
|
|
1069
|
+
// 工程1.5: 正本を直接読む(op=state v2 の secretsNow / capabilitiesNow / githubOwners)
|
|
1070
|
+
//
|
|
1071
|
+
// 🔴 **これは React 移行(#224)までの止血パッチ。** 差し替えるのは「読む先」だけで、
|
|
1072
|
+
// 4層(静的 → まとめ表 → registeredNow → stateNow)の合成そのものには手を付けない。
|
|
1073
|
+
// 捨てる予定のコードなので、ここを起点に整理を始めない。
|
|
1074
|
+
//
|
|
1075
|
+
// 3つに共通する規則:
|
|
1076
|
+
// 1. **読めなかったら従来のまとめ表の値へ落ちる。** 画面と Lambda は別々に配られるので、
|
|
1077
|
+
// 新しいキーを返さない応答を読む期間が必ずある
|
|
1078
|
+
// 2. **"unknown"(確かめられなかった)を「無い」に丸めない。** 鍵が入っている案件に
|
|
1079
|
+
// 「登録してください」と言うのが、この修正がまさに殺そうとしている嘘(#227)
|
|
1080
|
+
// 3. ライブ専用キーは build.ts の許可リストの網にかからないので、**画面側でも通す**
|
|
1081
|
+
// ---------------------------------------------------------------------------
|
|
1082
|
+
/**
|
|
1083
|
+
* 応答のキー(ASCII)→ 画面の能力名。**registeredNow の対応表に会議を足したもの。**
|
|
1084
|
+
*
|
|
1085
|
+
* 会議は登録の一覧を返さない(`REGISTERED_FIELDS` に無い)が、**能力の状態は持つ**
|
|
1086
|
+
* (`ingest-config.json` を読んだ結果)。対応表を分けると、能力を足したときに
|
|
1087
|
+
* 片方だけ書き足す形になるので、こちらは向こうを取り込んで1箇所に保つ。
|
|
1088
|
+
*/
|
|
1089
|
+
const STATE_CAP_NAMES = Object.assign({}, REGISTERED_CAP_NAMES, { meetings: "会議" });
|
|
1090
|
+
/** 鍵の登録状況(列2)の語彙。サーバ側 `secretsNowRow` と同じ(増やさない) */
|
|
1091
|
+
const SECRET_NOW_STATES = ["registered", "missing", "unknown"];
|
|
1092
|
+
/**
|
|
1093
|
+
* `secretsNow`(鍵が登録されているか・列2)の許可リスト。
|
|
1094
|
+
*
|
|
1095
|
+
* 応答の1行は `{ adapter, kind, state, source, space? }`。画面が使うのは kind / state /
|
|
1096
|
+
* space だけで、**adapter は使わない**(鍵カードは種別で引く)。source は「どの棚が
|
|
1097
|
+
* 答えたか」の内部語なので、画面に出す語へ畳むのは読み出し側(`secretStateFromNow`)。
|
|
1098
|
+
*/
|
|
1099
|
+
function sanitizeSecretsNow(v) {
|
|
1100
|
+
if (!Array.isArray(v))
|
|
1101
|
+
return null;
|
|
1102
|
+
const out = [];
|
|
1103
|
+
for (const r of v) {
|
|
1104
|
+
if (out.length >= 50)
|
|
1105
|
+
break;
|
|
1106
|
+
if (r == null || typeof r !== "object" || Array.isArray(r))
|
|
1107
|
+
continue;
|
|
1108
|
+
const kind = typeof r.kind === "string" ? r.kind : "";
|
|
1109
|
+
const state = typeof r.state === "string" ? r.state : "";
|
|
1110
|
+
// 知らない語は「未登録」に化けさせず落とす(落ちればまとめ表へフォールバックする)
|
|
1111
|
+
if (!kind || SECRET_NOW_STATES.indexOf(state) < 0)
|
|
1112
|
+
continue;
|
|
1113
|
+
const row = { kind, state };
|
|
1114
|
+
// 🔴 **スペースは小文字に畳む。** 突き合わせる相手(画面の行のドメイン)が小文字なので、
|
|
1115
|
+
// 揃えないと**入っている鍵を「未登録」と言う**(`issuesRefOf` と同じ理由)
|
|
1116
|
+
if (typeof r.space === "string" && r.space)
|
|
1117
|
+
row.space = r.space.toLowerCase();
|
|
1118
|
+
out.push(row);
|
|
1119
|
+
}
|
|
1120
|
+
return out.length ? out : null;
|
|
1121
|
+
}
|
|
1122
|
+
/**
|
|
1123
|
+
* `secretsNow` から、その鍵カードの答えを引く。**答えられないときは null。**
|
|
1124
|
+
*
|
|
1125
|
+
* null は「まとめ表の値へ落ちる」の合図で、次の3つで返る:
|
|
1126
|
+
* - その種別の行が1つも来ていない(古い Lambda・鍵を持たないアダプタ)
|
|
1127
|
+
* - スペース別に聞かれたのに、そのスペースの行が来ていない
|
|
1128
|
+
* - `unknown`(権限・スロットリングで確かめられなかった)
|
|
1129
|
+
*
|
|
1130
|
+
* @param space Backlog のスペース(画面の行のドメイン)。他の鍵では空
|
|
1131
|
+
*/
|
|
1132
|
+
function secretStateFromNow(rows, kind, space) {
|
|
1133
|
+
if (!Array.isArray(rows) || !rows.length || !kind)
|
|
1134
|
+
return null;
|
|
1135
|
+
const mine = rows.filter((r) => r && r.kind === kind);
|
|
1136
|
+
if (!mine.length)
|
|
1137
|
+
return null;
|
|
1138
|
+
let hit = null;
|
|
1139
|
+
if (space) {
|
|
1140
|
+
hit = mine.filter((r) => r.space === String(space).toLowerCase())[0] || null;
|
|
1141
|
+
if (!hit)
|
|
1142
|
+
return null;
|
|
1143
|
+
}
|
|
1144
|
+
else {
|
|
1145
|
+
// **1行に決まるときだけ答える。** スペースが複数ある Backlog を種別だけで聞かれたら、
|
|
1146
|
+
// どの棚のことか決まらない——片方だけ入れた状態を「登録済み」と言うことになる
|
|
1147
|
+
if (mine.length > 1)
|
|
1148
|
+
return null;
|
|
1149
|
+
hit = mine[0];
|
|
1150
|
+
}
|
|
1151
|
+
if (hit.state === "registered")
|
|
1152
|
+
return { known: "yes", src: "Secrets Manager" };
|
|
1153
|
+
if (hit.state === "missing")
|
|
1154
|
+
return { known: "no", src: "" };
|
|
1155
|
+
return null;
|
|
1156
|
+
}
|
|
1157
|
+
/**
|
|
1158
|
+
* その種別の鍵が**どこかに**登録されているか(タイルの「⚠ トークンが未設定です」用)。
|
|
1159
|
+
*
|
|
1160
|
+
* タイルは能力に1枚しかないので、スペース別の答えは持てない。**まとめ表の
|
|
1161
|
+
* `secretSources[kind]`(種別ごとの1つの値)と同じ粒度**に畳む——1本でも入っていれば
|
|
1162
|
+
* 注記を出さない。スペースごとの充足は行の中の鍵カードが答える。
|
|
1163
|
+
*
|
|
1164
|
+
* @returns "yes" | "no" | "unknown"(unknown はまとめ表へフォールバック)
|
|
1165
|
+
*/
|
|
1166
|
+
function secretRegisteredAnywhere(rows, kind) {
|
|
1167
|
+
if (!Array.isArray(rows) || !kind)
|
|
1168
|
+
return "unknown";
|
|
1169
|
+
const mine = rows.filter((r) => r && r.kind === kind);
|
|
1170
|
+
if (!mine.length)
|
|
1171
|
+
return "unknown";
|
|
1172
|
+
if (mine.some((r) => r.state === "registered"))
|
|
1173
|
+
return "yes";
|
|
1174
|
+
if (mine.every((r) => r.state === "missing"))
|
|
1175
|
+
return "no";
|
|
1176
|
+
return "unknown";
|
|
1177
|
+
}
|
|
1178
|
+
/** 能力の状態の語彙。サーバ側 `CAPABILITY_STATES` と同じ(増やさない) */
|
|
1179
|
+
const CAPABILITY_NOW_STATES = ["on", "off", "empty", "broken", "unset"];
|
|
1180
|
+
/**
|
|
1181
|
+
* `capabilitiesNow`(能力が配線されているか)を、**まとめ表の `capabilities` と同じ形**
|
|
1182
|
+
* (能力名=日本語のキー)に畳む。読めた能力だけを返し、1つも無ければ null。
|
|
1183
|
+
*
|
|
1184
|
+
* ⚠ **応答は `caps` で要求した能力ぶんしか来ない**(画面が開いた能力だけを読む・
|
|
1185
|
+
* `OPEN_CAPS` の項)。だから**キー単位でまとめ表へ落ちる**——ここだけは
|
|
1186
|
+
* `rawFleetVal` の「キーごとにフォールバックしない」の例外で、そうしないと
|
|
1187
|
+
* 開いていない能力のタイルが全部「未接続」に落ちる。
|
|
1188
|
+
*/
|
|
1189
|
+
function sanitizeCapabilitiesNow(v) {
|
|
1190
|
+
if (!v || typeof v !== "object" || Array.isArray(v))
|
|
1191
|
+
return null;
|
|
1192
|
+
const out = {};
|
|
1193
|
+
let any = false;
|
|
1194
|
+
for (const id in STATE_CAP_NAMES) {
|
|
1195
|
+
if (!Object.prototype.hasOwnProperty.call(STATE_CAP_NAMES, id))
|
|
1196
|
+
continue;
|
|
1197
|
+
const c = v[id];
|
|
1198
|
+
if (c == null || typeof c !== "object" || Array.isArray(c))
|
|
1199
|
+
continue;
|
|
1200
|
+
const state = typeof c.state === "string" ? c.state : "";
|
|
1201
|
+
if (CAPABILITY_NOW_STATES.indexOf(state) < 0)
|
|
1202
|
+
continue;
|
|
1203
|
+
const row = { state };
|
|
1204
|
+
if (typeof c.adapter === "string" && c.adapter)
|
|
1205
|
+
row.adapter = c.adapter;
|
|
1206
|
+
if (typeof c.detail === "string" && c.detail)
|
|
1207
|
+
row.detail = c.detail;
|
|
1208
|
+
out[STATE_CAP_NAMES[id]] = row;
|
|
1209
|
+
any = true;
|
|
1210
|
+
}
|
|
1211
|
+
return any ? out : null;
|
|
1212
|
+
}
|
|
1213
|
+
/**
|
|
1214
|
+
* `registeredNow.githubOwners`(組織ごとの App の導入状況・#233)の許可リスト。
|
|
1215
|
+
*
|
|
1216
|
+
* 🔴 **`appInstalled` は `=== true` / `=== false` / それ以外は null の厳格正規化。**
|
|
1217
|
+
* サーバ側(`withGithubInstallations`)が同じ畳み方をしているが、**画面でも畳む**
|
|
1218
|
+
* ——生の値(インストールトークンが入りうる)を素通しする経路をどちらにも作らない。
|
|
1219
|
+
* null は「その回は確かめられなかった」で、**どちらの案内も出させない値**。
|
|
1220
|
+
*/
|
|
1221
|
+
function sanitizeGithubOwnersNow(v) {
|
|
1222
|
+
if (!Array.isArray(v))
|
|
1223
|
+
return null;
|
|
1224
|
+
const out = [];
|
|
1225
|
+
for (const r of v) {
|
|
1226
|
+
if (out.length >= 50)
|
|
1227
|
+
break;
|
|
1228
|
+
if (r == null || typeof r !== "object" || Array.isArray(r))
|
|
1229
|
+
continue;
|
|
1230
|
+
const owner = typeof r.owner === "string" ? r.owner : "";
|
|
1231
|
+
if (!owner)
|
|
1232
|
+
continue;
|
|
1233
|
+
const row = { owner, appInstalled: r.appInstalled === true ? true : r.appInstalled === false ? false : null };
|
|
1234
|
+
if (typeof r.repos === "number" && Number.isFinite(r.repos) && r.repos >= 0)
|
|
1235
|
+
row.repos = r.repos;
|
|
1236
|
+
out.push(row);
|
|
1237
|
+
}
|
|
1238
|
+
return out.length ? out : null;
|
|
1239
|
+
}
|
|
1240
|
+
/**
|
|
1241
|
+
* サーバが返した「次の一手」(`needed`)を、素の文字列と URL に切り分ける。
|
|
1242
|
+
*
|
|
1243
|
+
* 🔴 **正規表現をクライアント側に書き写さないこと。** 埋め込みJSはテンプレート
|
|
1244
|
+
* リテラルの中なので、`\/` や `\s` がリテラルの解釈で潰れて**別物の正規表現**になる
|
|
1245
|
+
* (`backlogTargetOf` の項に、実際にクライアントJSが丸ごと構文エラーで落ちた経緯がある)。
|
|
1246
|
+
* ここで切り分けて、画面側は DOM を組み立てるだけにする。
|
|
1247
|
+
*
|
|
1248
|
+
* @returns [{ text, url }] の配列。url:true の部分だけをリンクにする
|
|
1249
|
+
*/
|
|
1250
|
+
function splitNeededParts(text) {
|
|
1251
|
+
const s = String(text == null ? "" : text);
|
|
1252
|
+
const out = [];
|
|
1253
|
+
// 日本語の文の途中に URL が挟まる(「… https://… で追加してください」)。
|
|
1254
|
+
// 空白と句読点で切る——読点・句点まで飲み込むと、開けないリンクになる
|
|
1255
|
+
const re = /https?:\/\/[^\s、。]+/g;
|
|
1256
|
+
let at = 0;
|
|
1257
|
+
let m;
|
|
1258
|
+
while ((m = re.exec(s)) !== null) {
|
|
1259
|
+
if (m.index > at)
|
|
1260
|
+
out.push({ text: s.slice(at, m.index), url: false });
|
|
1261
|
+
out.push({ text: m[0], url: true });
|
|
1262
|
+
at = m.index + m[0].length;
|
|
1263
|
+
}
|
|
1264
|
+
if (at < s.length)
|
|
1265
|
+
out.push({ text: s.slice(at), url: false });
|
|
1266
|
+
return out;
|
|
1267
|
+
}
|
|
1268
|
+
/**
|
|
1269
|
+
* `stateNow` から、その能力の「最後に取り込めた時刻」を引く(#227 の時刻の一貫化)。
|
|
1270
|
+
*
|
|
1271
|
+
* **「最終同期」(`internalSources.lastSync`)より優先する。** あちらは run 全体の
|
|
1272
|
+
* 最終成功時刻(空振りした run も含む run 単位のスナップショット)で、こちらは
|
|
1273
|
+
* **接続先ごとに実際に取り込めた事実**。同じ行に2つの時刻の意味を混ぜない。
|
|
1274
|
+
*
|
|
1275
|
+
* **成功の時刻だけを採る。** 失敗した行の `at` は「最後に試した時刻」で、
|
|
1276
|
+
* 「最終同期」として出すと**取り込めていないのに同期できたように読める**。
|
|
1277
|
+
*
|
|
1278
|
+
* @param capId 応答側の能力名(issues / design / materials / meetings)
|
|
1279
|
+
* @returns ISO文字列。1件も無ければ ""
|
|
1280
|
+
*/
|
|
1281
|
+
function ingestedAtOf(rows, capId) {
|
|
1282
|
+
if (!Array.isArray(rows) || !capId)
|
|
1283
|
+
return "";
|
|
1284
|
+
let best = "";
|
|
1285
|
+
for (const r of rows) {
|
|
1286
|
+
if (!r || r.cap !== capId || r.question !== "ingested")
|
|
1287
|
+
continue;
|
|
1288
|
+
const at = r.lastOkAt || (r.status === "ok" ? r.at : "");
|
|
1289
|
+
if (typeof at === "string" && at > best)
|
|
1290
|
+
best = at;
|
|
1291
|
+
}
|
|
1292
|
+
return best;
|
|
1293
|
+
}
|
|
1061
1294
|
function capabilityState(cap, ctx) {
|
|
1062
1295
|
// **能力→パイプラインの対応はここに持つ。** 呼び出し側に渡させると、
|
|
1063
1296
|
// クライアント側(この関数を .toString() で受け取る側)が同じ表を書き写すことになり、
|
|
@@ -2386,6 +2619,18 @@ var STATE_ROW_FIELDS = ${JSON.stringify(STATE_ROW_FIELDS)};
|
|
|
2386
2619
|
${sanitizeStateNow.toString()}
|
|
2387
2620
|
${overlayStateOnFleet.toString()}
|
|
2388
2621
|
${nextStateDelay.toString()}
|
|
2622
|
+
// 工程1.5: 正本を直接読む(secretsNow / capabilitiesNow / githubOwners)。
|
|
2623
|
+
// **判定の実体はビルド時の定義**——画面側に書き写すと許可リストが2枚になる
|
|
2624
|
+
var STATE_CAP_NAMES = ${JSON.stringify(STATE_CAP_NAMES)};
|
|
2625
|
+
var SECRET_NOW_STATES = ${JSON.stringify(SECRET_NOW_STATES)};
|
|
2626
|
+
var CAPABILITY_NOW_STATES = ${JSON.stringify(CAPABILITY_NOW_STATES)};
|
|
2627
|
+
${sanitizeSecretsNow.toString()}
|
|
2628
|
+
${secretStateFromNow.toString()}
|
|
2629
|
+
${secretRegisteredAnywhere.toString()}
|
|
2630
|
+
${sanitizeCapabilitiesNow.toString()}
|
|
2631
|
+
${sanitizeGithubOwnersNow.toString()}
|
|
2632
|
+
${splitNeededParts.toString()}
|
|
2633
|
+
${ingestedAtOf.toString()}
|
|
2389
2634
|
// 「はじめに」セクションのチュートリアルデータ(ビルド時に埋め込み。ボタンURL・チャレンジ4の決定は解決済み)
|
|
2390
2635
|
var TUTORIAL = (function () {
|
|
2391
2636
|
var elx = document.getElementById("tutorial-data");
|
|
@@ -3327,7 +3572,7 @@ ${nextStateDelay.toString()}
|
|
|
3327
3572
|
*/
|
|
3328
3573
|
/** 台帳から、その組織の情報を引く(無ければ null=断定しない) */
|
|
3329
3574
|
function githubOwnerMeta(owner) {
|
|
3330
|
-
var list =
|
|
3575
|
+
var list = githubOwnersView() || [];
|
|
3331
3576
|
for (var i = 0; i < list.length; i++) {
|
|
3332
3577
|
if (list[i] && list[i].owner === owner) return list[i];
|
|
3333
3578
|
}
|
|
@@ -3358,7 +3603,7 @@ ${nextStateDelay.toString()}
|
|
|
3358
3603
|
// どちらを先に出しても片側だけ新しい期間ができる。その間に 「token」 を落とすと、
|
|
3359
3604
|
// コミット済みの値を読んだ画面が偽の警告を出す。**外すのはフェーズ2**(engine の
|
|
3360
3605
|
// v1 が全案件へ行き渡ったことを確かめてから)。
|
|
3361
|
-
if (o) {
|
|
3606
|
+
if (o && o.source) {
|
|
3362
3607
|
state = (o.source === "app" || o.source === "workflow" || o.source === "token") ? "ok"
|
|
3363
3608
|
: o.source === "unknown" ? "unsure"
|
|
3364
3609
|
: "warn";
|
|
@@ -3369,6 +3614,50 @@ ${nextStateDelay.toString()}
|
|
|
3369
3614
|
}
|
|
3370
3615
|
g.items.push(s);
|
|
3371
3616
|
});
|
|
3617
|
+
// 🔴 **「source」 を持たない行(op=state の registeredNow.githubOwners・工程1.5)は
|
|
3618
|
+
// 「appInstalled」 と行の実測から決める。** PR-B でまとめ表から 「githubOwners」 が
|
|
3619
|
+
// 消えたため、ここが無いと #233 の出し分け(App 未導入 / リポジトリ未選択)が
|
|
3620
|
+
// 画面から消える。**行の判定を数え直すのは、この関数がそれを持っているから**
|
|
3621
|
+
// ——「items」 は既に stateNow を重ねた後の行(overlayStateOnFleet)なので、
|
|
3622
|
+
// 「押して確かめた結果」もそのまま効く。
|
|
3623
|
+
//
|
|
3624
|
+
// ⚠ **まとめ表の行(「source」 を持つ)には触らない。** あちらは夜間が数えた
|
|
3625
|
+
// 「repos」 / 「unreadable」 を持っており、そちらが件数の正本
|
|
3626
|
+
order.forEach(function (g) {
|
|
3627
|
+
var o = g.meta;
|
|
3628
|
+
if (!o || o.source) return;
|
|
3629
|
+
// 読めていないリポジトリの数。**測った行だけを数える**——一度も測っていない行を
|
|
3630
|
+
// 「読めていない」に数えると、立ち上げ直後の組織に偽の警告を出す
|
|
3631
|
+
var seen = {};
|
|
3632
|
+
var shown = 0;
|
|
3633
|
+
var unread = 0;
|
|
3634
|
+
g.items.forEach(function (s) {
|
|
3635
|
+
var ref = String(s.ref || "");
|
|
3636
|
+
// 同じリポジトリの issues と discussions は1つと数える(行は2本でも接続は1つ)
|
|
3637
|
+
if (!ref || seen[ref]) return;
|
|
3638
|
+
seen[ref] = 1;
|
|
3639
|
+
shown++;
|
|
3640
|
+
if (s.gate && s.gate !== "ok") unread++;
|
|
3641
|
+
});
|
|
3642
|
+
// 総数は登録の件数(正本が数えたもの)を優先する。画面に出ている行は
|
|
3643
|
+
// フィルタや読み込み途中でこれより少ないことがある
|
|
3644
|
+
g.repos = o.repos != null ? o.repos : shown;
|
|
3645
|
+
if (o.appInstalled === false) {
|
|
3646
|
+
// App が入っていない=この組織のリポジトリは1つも読めない
|
|
3647
|
+
g.state = "warn";
|
|
3648
|
+
g.unread = g.repos;
|
|
3649
|
+
return;
|
|
3650
|
+
}
|
|
3651
|
+
if (o.appInstalled === true) {
|
|
3652
|
+
// 入っているのに読めない行がある=そのリポジトリが読み取り対象に選ばれていない
|
|
3653
|
+
g.state = unread > 0 ? "warn" : "ok";
|
|
3654
|
+
g.unread = unread;
|
|
3655
|
+
return;
|
|
3656
|
+
}
|
|
3657
|
+
// 「null」(確かめられなかった)。**どちらの案内も出さない**——押しても直るとは
|
|
3658
|
+
// 限らないし、「未接続」と言えば入っている組織に偽の警告を出す
|
|
3659
|
+
g.state = null;
|
|
3660
|
+
});
|
|
3372
3661
|
// **繋がっている組織を先に、繋がっていない組織を後に。**
|
|
3373
3662
|
// 直すものが無い人にも「未接続」が最初に見えるのは、優先順位が逆
|
|
3374
3663
|
order.sort(function (a, b) {
|
|
@@ -3385,7 +3674,7 @@ ${nextStateDelay.toString()}
|
|
|
3385
3674
|
* どの組織かは**行のラベル**が示し、**接続が要る組だけ**案内が行を抱える。
|
|
3386
3675
|
*/
|
|
3387
3676
|
function githubRows(items) {
|
|
3388
|
-
var owners =
|
|
3677
|
+
var owners = githubOwnersView();
|
|
3389
3678
|
var out = [];
|
|
3390
3679
|
groupGithubByOwner(items, owners).forEach(function (g) {
|
|
3391
3680
|
// **行ごとに meta を引く。** issues と discussions でアイコンとラベルが違う
|
|
@@ -3447,8 +3736,11 @@ ${nextStateDelay.toString()}
|
|
|
3447
3736
|
function githubNotConnectedNodes(g) {
|
|
3448
3737
|
var out = [];
|
|
3449
3738
|
var o = g.meta || {};
|
|
3450
|
-
|
|
3451
|
-
|
|
3739
|
+
// **件数はグループが数えたものを優先する(工程1.5)。** まとめ表の 「repos」 /
|
|
3740
|
+
// 「unreadable」 は夜間の値で、いま画面に出ている行の数と食い違うことがある
|
|
3741
|
+
//(#101 で3度直した形)。「groupGithubByOwner」 は**いま並んでいる行**を数えている
|
|
3742
|
+
var repos = o.repos != null ? o.repos : g.repos != null ? g.repos : g.items.length;
|
|
3743
|
+
var unread = o.unreadable != null ? o.unreadable : g.unread != null ? g.unread : g.items.length;
|
|
3452
3744
|
var installed = o.appInstalled;
|
|
3453
3745
|
out.push(el("div", { class: "ds-orgnote-title", text: "⚠️ " + g.title + " はまだ接続されていません" }));
|
|
3454
3746
|
// **読める件数を出す。** 総数だけ出すと、同じ画面の集計と食い違う(#101 で3度直した形)
|
|
@@ -3783,6 +4075,30 @@ ${nextStateDelay.toString()}
|
|
|
3783
4075
|
var LIVE_REGISTERED = null;
|
|
3784
4076
|
// 接続状態ストアの行(届くか・取り込めたか)。op=state の stateNow から
|
|
3785
4077
|
var LIVE_STATE = null;
|
|
4078
|
+
/**
|
|
4079
|
+
* 鍵がいま登録されているか(op=state の secretsNow・列2)。取得前・取得失敗時は null。
|
|
4080
|
+
*
|
|
4081
|
+
* 🔴 **まとめ表の secretSources / secretSpaces より必ず優先する。** あちらは夜間の
|
|
4082
|
+
* スナップショットなので、**鍵を保存した直後は前日の「未登録」を出し続ける**
|
|
4083
|
+
* ——「保存したのに未登録」(#227)の正体。こちらは Secrets Manager を都度見た答え。
|
|
4084
|
+
*/
|
|
4085
|
+
var LIVE_SECRETS = null;
|
|
4086
|
+
/**
|
|
4087
|
+
* 能力が配線されているか(op=state の capabilitiesNow)。取得前・取得失敗時は null。
|
|
4088
|
+
*
|
|
4089
|
+
* **形はまとめ表の capabilities と同じ**(能力名のキー・{adapter, state, detail})なので、
|
|
4090
|
+
* 読む側は capsView() で重ねるだけでよい。**要求した能力ぶんしか来ない**ので、
|
|
4091
|
+
* 欠けている能力はまとめ表の値で描く(sanitizeCapabilitiesNow の項)。
|
|
4092
|
+
*/
|
|
4093
|
+
var LIVE_CAPS = null;
|
|
4094
|
+
/**
|
|
4095
|
+
* 組織ごとの GitHub App の導入状況(op=state の registeredNow.githubOwners・#233)。
|
|
4096
|
+
*
|
|
4097
|
+
* **registeredNow の他のキーとは別に持つ。** あちらは「まとめ表がまだ知らない行を足す」
|
|
4098
|
+
* ための材料で、こちらは組織の状態そのもの。同じ入れ物に混ぜると、行の追加の判定
|
|
4099
|
+
* (unsettled/pending)に組織の行が紛れ込む。
|
|
4100
|
+
*/
|
|
4101
|
+
var LIVE_GH_OWNERS = null;
|
|
3786
4102
|
/** マージ済みの3キー(作り直しは setLive* だけが行う) */
|
|
3787
4103
|
var MERGED = null;
|
|
3788
4104
|
/** マージの対象になるキー。これ以外は素の値をそのまま返す */
|
|
@@ -3825,6 +4141,55 @@ ${nextStateDelay.toString()}
|
|
|
3825
4141
|
return rawFleetVal(key);
|
|
3826
4142
|
}
|
|
3827
4143
|
|
|
4144
|
+
/**
|
|
4145
|
+
* 能力の状態(capabilities)。**能力ごとに、正本(capabilitiesNow)があればそちら。**
|
|
4146
|
+
*
|
|
4147
|
+
* ⚠ **ここは rawFleetVal の「キー単位でフォールバックしない」の意図的な例外**(工程1.5)。
|
|
4148
|
+
* あちらの禁止は「同じ画面に2つの時点が混ざると、上に出す『◯◯時点』が嘘になる」ため
|
|
4149
|
+
* だが、capabilitiesNow は**画面が開いた能力ぶんしか来ない**(OPEN_CAPS の項)ので、
|
|
4150
|
+
* 丸ごと採るか丸ごと捨てるかしか無いと、**開いていない能力のタイルが全部「未接続」に
|
|
4151
|
+
* 落ちる**。能力ごとに正本を優先し、来ていない能力はまとめ表のまま出す。
|
|
4152
|
+
*/
|
|
4153
|
+
function capsView() {
|
|
4154
|
+
var base = fleetVal("capabilities") || {};
|
|
4155
|
+
if (!LIVE_CAPS) return base;
|
|
4156
|
+
var out = {};
|
|
4157
|
+
for (var k in base) if (Object.prototype.hasOwnProperty.call(base, k)) out[k] = base[k];
|
|
4158
|
+
for (var n in LIVE_CAPS) if (Object.prototype.hasOwnProperty.call(LIVE_CAPS, n)) out[n] = LIVE_CAPS[n];
|
|
4159
|
+
return out;
|
|
4160
|
+
}
|
|
4161
|
+
|
|
4162
|
+
/**
|
|
4163
|
+
* 組織ごとの接続状況(#233 の出し分けの材料)。
|
|
4164
|
+
*
|
|
4165
|
+
* **まとめ表からは消えた(PR-B)。** fleet-status.json の専用プローブを廃止したので、
|
|
4166
|
+
* いまの供給元は op=state の 「registeredNow.githubOwners」(App の導入状況の実測)。
|
|
4167
|
+
* 古い生成物を読んでいる案件のために、まとめ表の行も残す(組織ごとに正本が勝つ)。
|
|
4168
|
+
*/
|
|
4169
|
+
function githubOwnersView() {
|
|
4170
|
+
var fleet = fleetVal("githubOwners");
|
|
4171
|
+
if (!LIVE_GH_OWNERS) {
|
|
4172
|
+
// **「対象0件と確かめた」だけは正本から言える。** 開発の登録一覧が読めていて0件なら、
|
|
4173
|
+
// 組織の行が1つも無いのは当たり前(下の「まだありません」の1行はこれで出る)。
|
|
4174
|
+
// まとめ表を持たない案件で 「undefined」(まだ走っていない)と混ぜない
|
|
4175
|
+
if (!fleet && LIVE_REGISTERED && Array.isArray(LIVE_REGISTERED.dev) && LIVE_REGISTERED.dev.length === 0) return [];
|
|
4176
|
+
return fleet;
|
|
4177
|
+
}
|
|
4178
|
+
var out = [];
|
|
4179
|
+
var byOwner = {};
|
|
4180
|
+
for (var i = 0; i < LIVE_GH_OWNERS.length; i++) {
|
|
4181
|
+
byOwner[LIVE_GH_OWNERS[i].owner] = 1;
|
|
4182
|
+
out.push(LIVE_GH_OWNERS[i]);
|
|
4183
|
+
}
|
|
4184
|
+
// 正本が知らない組織(まとめ表にしか無い行)は落とさない——消すと、その組織の
|
|
4185
|
+
// 案内だけが画面から黙って消える
|
|
4186
|
+
var old = Array.isArray(fleet) ? fleet : [];
|
|
4187
|
+
for (var j = 0; j < old.length; j++) {
|
|
4188
|
+
if (old[j] && old[j].owner && !byOwner[old[j].owner]) out.push(old[j]);
|
|
4189
|
+
}
|
|
4190
|
+
return out;
|
|
4191
|
+
}
|
|
4192
|
+
|
|
3828
4193
|
/** まとめ表がまだ答えていない行の数(取り直しを続けるかの判断) */
|
|
3829
4194
|
function unsettledNow() {
|
|
3830
4195
|
return unsettledRegisteredCount(rawView(), LIVE_REGISTERED);
|
|
@@ -3903,7 +4268,10 @@ ${nextStateDelay.toString()}
|
|
|
3903
4268
|
String(rawFleetVal("generatedAt") || ""),
|
|
3904
4269
|
);
|
|
3905
4270
|
return {
|
|
3906
|
-
|
|
4271
|
+
// 🔴 **能力の状態は正本(capabilitiesNow)を通す(工程1.5)。** タイルの語は
|
|
4272
|
+
// capsView から引いているので、ここを素のまとめ表のままにすると
|
|
4273
|
+
// 「タイルの文字は未接続・状態の色は接続OK」が同じタイルの中で並ぶ
|
|
4274
|
+
capabilities: capsView(),
|
|
3907
4275
|
internals: rawFleetVal("internalSources") || [],
|
|
3908
4276
|
externals: ov.externalSources || [],
|
|
3909
4277
|
pipes: rawFleetVal("pipelines") || [],
|
|
@@ -4398,7 +4766,7 @@ ${nextStateDelay.toString()}
|
|
|
4398
4766
|
if (noteIsUnset) reflectMark(noteEl, cap, "desc", function () {
|
|
4399
4767
|
// 足した行があるなら、この一文(接続先が設定されていません)はもう事実ではない
|
|
4400
4768
|
if (capPending(cap)) return { text: "", hidden: true };
|
|
4401
|
-
var capsNow =
|
|
4769
|
+
var capsNow = capsView();
|
|
4402
4770
|
var dNow = capabilityDisplayState(cap, capsNow);
|
|
4403
4771
|
var infoNow = capsNow[cap];
|
|
4404
4772
|
var t = dNow && dNow !== "on" && infoNow && infoNow.detail
|
|
@@ -4457,7 +4825,9 @@ ${nextStateDelay.toString()}
|
|
|
4457
4825
|
var sources = fleetVal("externalSources") || [];
|
|
4458
4826
|
var pipes = fleetVal("pipelines") || [];
|
|
4459
4827
|
// 能力ごとの導出済みの状態(#125② の正本)。持たない古いデータでは {}
|
|
4460
|
-
|
|
4828
|
+
// **読む先は capsView(工程1.5)。** 能力ごとに、正本(capabilitiesNow)があれば
|
|
4829
|
+
// そちら・来ていない能力はまとめ表のまま
|
|
4830
|
+
var caps = capsView();
|
|
4461
4831
|
var hasCaps = Object.keys(caps).length > 0;
|
|
4462
4832
|
// **この節に入るものが1つでもあるなら出す。**
|
|
4463
4833
|
//
|
|
@@ -4784,7 +5154,7 @@ ${nextStateDelay.toString()}
|
|
|
4784
5154
|
));
|
|
4785
5155
|
if (liveListWanted("github")) pushCap("開発", liveList(
|
|
4786
5156
|
"開発",
|
|
4787
|
-
function () { return externalsSig("github",
|
|
5157
|
+
function () { return externalsSig("github", githubOwnersView() || null); },
|
|
4788
5158
|
function (box) {
|
|
4789
5159
|
var items = liveExternals("github");
|
|
4790
5160
|
if (!items.length) return;
|
|
@@ -4837,7 +5207,7 @@ ${nextStateDelay.toString()}
|
|
|
4837
5207
|
//
|
|
4838
5208
|
// **「[]」(夜間バッチは走ったが対象0件)と 「undefined」(まだ走っていない)は別物。**
|
|
4839
5209
|
// 後者に「まだありません」と書くと、実際には登録済みかもしれない案件に嘘を言う
|
|
4840
|
-
var ownersNow =
|
|
5210
|
+
var ownersNow = githubOwnersView();
|
|
4841
5211
|
if (Array.isArray(ownersNow) && ownersNow.length === 0) {
|
|
4842
5212
|
pushCap("開発", el("p", {
|
|
4843
5213
|
class: "sec-how ds-all-connected",
|
|
@@ -4948,14 +5318,9 @@ ${nextStateDelay.toString()}
|
|
|
4948
5318
|
// 両方に出すと、同じ失敗が1画面に2回並ぶ。実行状況はパイプライン節に任せ、
|
|
4949
5319
|
// ここでは直し方だけをその行に添える(下の renderPipelines で使う)
|
|
4950
5320
|
var PIPE_CHECK = { "update-gold": "nightly_decisionlog", "sync-backlog": "nightly_backlog", "sync-designs": "figma_sync" };
|
|
4951
|
-
//
|
|
4952
|
-
//
|
|
4953
|
-
//
|
|
4954
|
-
var CHECK_CAP = {
|
|
4955
|
-
backlog_secrets: "課題管理", backlog_synced: "課題管理",
|
|
4956
|
-
figma_token: "デザイン", figma_inventory: "デザイン",
|
|
4957
|
-
dev_sources_registered: "開発", dev_issues_derived: "開発",
|
|
4958
|
-
};
|
|
5321
|
+
// ⚠ **能力タイルへの振り分け表(CHECK_CAP)は撤去した(#230)。** タイルに長文の
|
|
5322
|
+
// 対処手順を出すのをやめたので、振り分け先が無い。チェックは全部「セットアップの残り」に
|
|
5323
|
+
// 並ぶ(実行状況の行に添える PIPE_CHECK だけが例外)
|
|
4959
5324
|
// 🔴 **鍵の欄を指すチェックは、鍵の欄が出ているときだけ出す(オーナー決定 2026-08-22)。**
|
|
4960
5325
|
// 「この画面の『Backlog のAPIキー』に貼ってください」は、接続先が0件で欄を畳んでいる
|
|
4961
5326
|
// あいだは**存在しない欄を指す案内**になる(最後の接続先を外した直後に実際に出た。
|
|
@@ -4967,13 +5332,12 @@ ${nextStateDelay.toString()}
|
|
|
4967
5332
|
var kc = KEY_POINTING_CHECKS[c.id];
|
|
4968
5333
|
return !(kc && keyTargetCount(kc) === 0);
|
|
4969
5334
|
});
|
|
4970
|
-
//
|
|
4971
|
-
|
|
4972
|
-
//
|
|
5335
|
+
// **どこかに出せたものを覚えておく。** 出せなかった分は下の「セットアップの残り」へ
|
|
5336
|
+
// 落とす——振り分けを間違えても、消えるよりは出る方がよい。
|
|
5337
|
+
//
|
|
5338
|
+
// ⚠ **能力タイルはもう立てない(#230)。** 立てていた頃の名残で名前が残っているが、
|
|
5339
|
+
// いま立てるのは実行状況の行(同じ run の未達を2箇所に出さないため)だけ
|
|
4973
5340
|
var shownOnTile = {};
|
|
4974
|
-
function checksFor(cap) {
|
|
4975
|
-
return allChecks.filter(function (c) { return CHECK_CAP[c.id] === cap; });
|
|
4976
|
-
}
|
|
4977
5341
|
/** 残りの1件を、直し方まで含めて1行にする */
|
|
4978
5342
|
function checkLine(c) {
|
|
4979
5343
|
var row = el("div", { class: "ds-check" });
|
|
@@ -5063,8 +5427,15 @@ ${nextStateDelay.toString()}
|
|
|
5063
5427
|
}
|
|
5064
5428
|
// **未接続・未使用のときは最終同期を出さない。** 空振りしたrunの時刻なので、
|
|
5065
5429
|
// 「未接続」と並べると「一度は繋がった」と読めてしまう
|
|
5066
|
-
|
|
5067
|
-
|
|
5430
|
+
//
|
|
5431
|
+
// 🔴 **接続状態ストアに取り込みの行があるなら、その時刻を優先する(工程1.5・#227)。**
|
|
5432
|
+
// 「internalSources.lastSync」 は run 全体の最終成功時刻で、**どの接続先のことか
|
|
5433
|
+
// 分からない**(鍵が無くて1件も取り込めていない run でも「成功」で終わる)。
|
|
5434
|
+
// ストアの行は「その能力の接続先が実際に取り込めた事実」なので、同じ「最終同期」の
|
|
5435
|
+
// 位置に出すならこちらが正しい。行が無ければ従来どおり run の時刻
|
|
5436
|
+
if (st !== "off" && st !== "idle") {
|
|
5437
|
+
var ingestedAt = ingestedAtOf(LIVE_STATE, capIdOf(cap));
|
|
5438
|
+
var t = dsFmtTime(ingestedAt || (internal && internal.lastSync) || "");
|
|
5068
5439
|
if (t) parts.push("最終同期 " + t);
|
|
5069
5440
|
}
|
|
5070
5441
|
return parts.join(" ");
|
|
@@ -5141,7 +5512,7 @@ ${nextStateDelay.toString()}
|
|
|
5141
5512
|
// 「⏳ 反映中」→「⦿ 未接続」になる——データが正しくなったあとに嘘をつく。
|
|
5142
5513
|
// 状態の語も色(ds-gate-*)も、判定はタイルを作るときと同じ式で引く
|
|
5143
5514
|
if (derived === "unset") reflectMark(subEl, cap, "sub", function (n) {
|
|
5144
|
-
var capsNow =
|
|
5515
|
+
var capsNow = capsView();
|
|
5145
5516
|
var insNow = fleetVal("internalSources") || [];
|
|
5146
5517
|
var srcsNow = fleetVal("externalSources") || [];
|
|
5147
5518
|
var dNow = adapter === capabilityAdapter(cap, capsNow) ? capabilityDisplayState(cap, capsNow) : "unset";
|
|
@@ -5191,21 +5562,21 @@ ${nextStateDelay.toString()}
|
|
|
5191
5562
|
// 導出が無いデータでは判断材料が無いので、注記を出す側(=入力欄と揃う側)に倒す
|
|
5192
5563
|
// 接続先0件の抑止は入力欄と**同じ関数**を通す(keyTargetCount の 🔴 不変条件)
|
|
5193
5564
|
var tokenRelevant = (derived != null ? derived !== "off" : true) && keyTargetCount(cap) > 0;
|
|
5194
|
-
if (tokenRelevant && sk && (SITE.secretKinds || []).indexOf(sk) >= 0 && !(
|
|
5565
|
+
if (tokenRelevant && sk && (SITE.secretKinds || []).indexOf(sk) >= 0 && !secretPresentForTile(sk)) {
|
|
5195
5566
|
body.appendChild(el("div", { class: "ds-tile-note", text: "⚠ トークンが未設定です" }));
|
|
5196
5567
|
}
|
|
5197
|
-
//
|
|
5198
|
-
//
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5568
|
+
// 🔴 **セットアップの残り(checks)の長文はタイルに出さない(#230・オーナー指摘)。**
|
|
5569
|
+
//
|
|
5570
|
+
// 出所は engine の夜間チェックの 「action」 欄で、**この設定画面が無かった頃の
|
|
5571
|
+
// 手順**(「Secrets に FIGMA_TOKEN を登録しても同じです」「Run workflow を
|
|
5572
|
+
// 実行すると取り込まれます」「手元の Claude Code なら /sync-designs」)が
|
|
5573
|
+
// そのまま流れ込んでいた——UIから登録させたいのに UI を迂回する裏口を案内し、
|
|
5574
|
+
// しかも鍵を保存すれば同期は自動で起きるので**半分は嘘**になっていた。
|
|
5575
|
+
//
|
|
5576
|
+
// **タイルは短い状態表示だけにする。** 何が足りないかは「⚠ トークンが未設定です」で
|
|
5577
|
+
// 足り、対処は開いたモーダルの中の案内が持つ。
|
|
5578
|
+
// **shownOnTile を立てない**ので、これらの行は下の「セットアップの残り」で拾われる
|
|
5579
|
+
//(消すのではなく、置き場所を1つに戻す)。文言そのものの手当ては engine 側の別issue。
|
|
5209
5580
|
// **id は能力ごとに1つ(先頭のアダプタ)。** 接続マップのノードの飛び先で、
|
|
5210
5581
|
// 同じ id が2つある不正なHTMLになると getElementById が思わぬ側を指す
|
|
5211
5582
|
var tile = el("button", { class: "ds-tile", type: "button", id: ai ? "ds-" + cap + "-" + adapter : "ds-" + cap }, [
|
|
@@ -5353,11 +5724,15 @@ ${nextStateDelay.toString()}
|
|
|
5353
5724
|
* DS_GATE_NO_TOKEN が持っていて、どちらも dsGateOf を通して取る。ここで文面を
|
|
5354
5725
|
* 書き写すと、**同じ行の状態バッジと確認結果で語が割れる**(この節で3回直した形)。
|
|
5355
5726
|
*
|
|
5356
|
-
* 他アダプタ(
|
|
5727
|
+
* 他アダプタ(Backlog / Figma / Drive)を足すときの拡張点:
|
|
5357
5728
|
* - 判定と語彙は infra/lambda/shared/slack-gate.mjs 側に足す(画面は DS_GATE のまま)
|
|
5358
5729
|
* - この節に増やすのは probeHelpNodes の「次の一手」だけ。**アダプタ固有の分岐を
|
|
5359
5730
|
* probeVerdict に入れない**(入れた瞬間に判定が画面側にも生まれる)
|
|
5360
|
-
* -
|
|
5731
|
+
* - 送る種別(probeTypeOf)と、行に出す条件(下の extSourceCard)に足す
|
|
5732
|
+
*
|
|
5733
|
+
* **開発(GitHub)は #234 で足した。** 判定は shared/github-gate.mjs、次の一手は
|
|
5734
|
+
* サーバの needed(理由と設定ページのURLを1文で持つ)をそのまま出している
|
|
5735
|
+
* ——画面に分岐が生まれないよう、言い分けを全部あちらに置いた
|
|
5361
5736
|
* ------------------------------------------------------------------------- */
|
|
5362
5737
|
|
|
5363
5738
|
/**
|
|
@@ -5372,6 +5747,50 @@ ${nextStateDelay.toString()}
|
|
|
5372
5747
|
return dsGateOf({ type: s.type, workspace: s.workspace, gate: r.gate });
|
|
5373
5748
|
}
|
|
5374
5749
|
|
|
5750
|
+
/**
|
|
5751
|
+
* 確認したいアダプタの種別(「op=probe」 の 「type」)。**画面の種別をそのまま送らない**
|
|
5752
|
+
* ——「github-issues」 / 「github-discussions」 は同じ1つのリポジトリの読み取りで、
|
|
5753
|
+
* サーバの受け口は 「github」 の1種別しか知らない(「PROBE_REF_RULES」)。
|
|
5754
|
+
*/
|
|
5755
|
+
function probeTypeOf(type) {
|
|
5756
|
+
if (String(type || "").indexOf("github") === 0) return "github";
|
|
5757
|
+
return type === "slack" ? "slack" : "";
|
|
5758
|
+
}
|
|
5759
|
+
|
|
5760
|
+
/**
|
|
5761
|
+
* サーバが返した 「needed」(次の一手。URL を含むことがある)を、リンク付きの1段落にする。
|
|
5762
|
+
*
|
|
5763
|
+
* 🔴 **innerHTML を使わない。** 「needed」 はサーバの文だが、そこには**組織名や
|
|
5764
|
+
* リポジトリ名(人が登録した値)が埋め込まれている**。HTML として流し込む経路を
|
|
5765
|
+
* 1本でも作ると、登録した文字列が画面のマークアップになる。テキストノードと
|
|
5766
|
+
* 「<a>」 の組み立てだけで作る(この画面の他の表示と同じ流儀)。
|
|
5767
|
+
*
|
|
5768
|
+
* **URL 以外は素のまま出す。** 言い換えると検索できないので、サーバの文言を削らない。
|
|
5769
|
+
*/
|
|
5770
|
+
function neededNode(text) {
|
|
5771
|
+
var p = el("p", { class: "ds-probe-help" });
|
|
5772
|
+
splitNeededParts(text).forEach(function (part) {
|
|
5773
|
+
if (part.url) p.appendChild(el("a", { href: part.text, target: "_blank", rel: "noopener", text: part.text }));
|
|
5774
|
+
else p.appendChild(document.createTextNode(part.text));
|
|
5775
|
+
});
|
|
5776
|
+
return p;
|
|
5777
|
+
}
|
|
5778
|
+
|
|
5779
|
+
/**
|
|
5780
|
+
* 開発(GitHub)の確認結果の本文(#233 / #234)。
|
|
5781
|
+
*
|
|
5782
|
+
* **理由と対処はサーバが1文で持っている**(「shared/github-gate.mjs」 の 「needed」)。
|
|
5783
|
+
* 「App が入っていない」と「App は入っているがこのリポジトリが選ばれていない」の
|
|
5784
|
+
* 言い分けも、設定ページの URL もあちらが決める——**画面で分岐を書き足さない**
|
|
5785
|
+
* (書いた瞬間に判定が2箇所になり、夜間の実測とボタンでずれる)。
|
|
5786
|
+
*/
|
|
5787
|
+
function githubProbeHelpNodes(r) {
|
|
5788
|
+
if (r.gate === "ok") return [];
|
|
5789
|
+
if (r.needed) return [neededNode(r.needed)];
|
|
5790
|
+
// needed が無いのは gate に落とし込めなかったとき。**生のエラーは隠さない**
|
|
5791
|
+
return [el("p", { class: "ds-probe-help", text: "GitHub の応答:" + (r.error || "不明") + "。時間をおいて再度お試しください。" })];
|
|
5792
|
+
}
|
|
5793
|
+
|
|
5375
5794
|
/**
|
|
5376
5795
|
* 判定ごとの表示。**成功は1行で終わらせ、失敗は「理由+次の一手」だけを残す。**
|
|
5377
5796
|
*
|
|
@@ -5394,6 +5813,7 @@ ${nextStateDelay.toString()}
|
|
|
5394
5813
|
*/
|
|
5395
5814
|
function probeHelpNodes(s, r) {
|
|
5396
5815
|
var p = function (t) { return el("p", { class: "ds-probe-help", text: t }); };
|
|
5816
|
+
if (probeTypeOf(s.type) === "github") return githubProbeHelpNodes(r);
|
|
5397
5817
|
// 成功。**1行で終わり。** 状態の語(「接続OK」)が既に出ているので本文は空
|
|
5398
5818
|
if (r.gate === "ok") return [];
|
|
5399
5819
|
if (r.gate === "not_in_channel") {
|
|
@@ -5515,7 +5935,9 @@ ${nextStateDelay.toString()}
|
|
|
5515
5935
|
msg.textContent = "確認中…";
|
|
5516
5936
|
at.textContent = "";
|
|
5517
5937
|
body.textContent = "";
|
|
5518
|
-
|
|
5938
|
+
// **種別はサーバの語彙に畳む**(github-issues / github-discussions → github)。
|
|
5939
|
+
// ワークスペースは Slack にしか無い概念なので、他アダプタでは載せない
|
|
5940
|
+
var payload = { type: probeTypeOf(s.type), ref: s.ref };
|
|
5519
5941
|
if (s.workspace) payload.workspace = s.workspace;
|
|
5520
5942
|
var pfx = intake.url + (intake.url.indexOf("?") === -1 ? "?" : "&");
|
|
5521
5943
|
try {
|
|
@@ -5547,8 +5969,10 @@ ${nextStateDelay.toString()}
|
|
|
5547
5969
|
var oldScopes = card.querySelectorAll(".ds-probe-scope");
|
|
5548
5970
|
for (var oi = 0; oi < oldScopes.length; oi++) oldScopes[oi].remove();
|
|
5549
5971
|
// 不足スコープは**状態の語のすぐ横**に出す(「権限が足りません:groups:read」)。
|
|
5550
|
-
//
|
|
5551
|
-
|
|
5972
|
+
// 本文へ落とすと、いちばん要る情報が説明文に埋もれる。
|
|
5973
|
+
// ⚠ **札にするのは Slack のスコープ名だけ。** GitHub の needed は URL 込みの
|
|
5974
|
+
// 一文なので、札に押し込むとバッジの横で折り返して読めない(本文へ出す)
|
|
5975
|
+
if (j.gate === "missing_scope" && probeTypeOf(s.type) === "slack") {
|
|
5552
5976
|
head.appendChild(el("code", { class: "ds-probe-scope", text: j.needed || "不明" }));
|
|
5553
5977
|
}
|
|
5554
5978
|
at.textContent = "いま測った結果(" + (dsFmtTime(j.checkedAt) || "たった今") + ")";
|
|
@@ -5557,8 +5981,10 @@ ${nextStateDelay.toString()}
|
|
|
5557
5981
|
// 下に残ると、同じ行に2つのことが書いてある状態になる
|
|
5558
5982
|
var stale = card.querySelector(".ds-gate-help");
|
|
5559
5983
|
if (stale) stale.hidden = true;
|
|
5560
|
-
// 表示名を Slack
|
|
5561
|
-
|
|
5984
|
+
// 表示名を Slack の実名へ自動で揃える(差分表示も更新ボタンも出さない)。
|
|
5985
|
+
// **GitHub は揃えない。** 行の名前は owner/repo そのもので、書き換える口
|
|
5986
|
+
//(op=config の refresh-channel-name)は channels.json 専用
|
|
5987
|
+
if (s.type === "slack") autoAlignName(s, j, card);
|
|
5562
5988
|
} catch (e) {
|
|
5563
5989
|
msg.className = "ds-toggle-msg is-error";
|
|
5564
5990
|
msg.textContent = "✗ 通信に失敗しました。";
|
|
@@ -7153,7 +7579,9 @@ ${nextStateDelay.toString()}
|
|
|
7153
7579
|
// 内部事情を全行に並べることになる——押す必要が無いものを説明する側の混雑。
|
|
7154
7580
|
// 一方 App の札は「この組織はもう繋いである」という**運用上の意味**を持つので残す。
|
|
7155
7581
|
var ghMeta = githubOwnerMeta(ghOwner);
|
|
7156
|
-
|
|
7582
|
+
// **正本(registeredNow.githubOwners)は source を持たない**(工程1.5)。
|
|
7583
|
+
// App が入っていると実測できたときが、まとめ表の source === "app" に当たる
|
|
7584
|
+
var via = ghMeta && (ghMeta.source === "app" || ghMeta.appInstalled === true) ? "(GitHub App)" : "";
|
|
7157
7585
|
badges.appendChild(el("span", {
|
|
7158
7586
|
class: "badge ds-workspace" + (ghMeta && ghMeta.source === "unknown" ? " ds-workspace-unknown" : ""),
|
|
7159
7587
|
text: ghOwner + via,
|
|
@@ -7260,13 +7688,15 @@ ${nextStateDelay.toString()}
|
|
|
7260
7688
|
// 気づいた人がその場で確かめられるほうがよい(オーナー判断)。書き込みになる
|
|
7261
7689
|
// 「表示名を更新する」は、押した結果の中から別のボタンとして出る(probeSection)。
|
|
7262
7690
|
//
|
|
7263
|
-
// **いまは Slack
|
|
7264
|
-
//
|
|
7265
|
-
//
|
|
7691
|
+
// **いまは Slack と 開発(GitHub)。** GitHub は #234 で足した——「アクセス不可 →
|
|
7692
|
+
// App の Configure でリポジトリを追加 → 直ったか確かめる」が、それまで**翌朝まで
|
|
7693
|
+
// 待つしかなかった**(受け入れテストでは fleet-status.yml を手で起動して確かめた)。
|
|
7694
|
+
// 判定と文面は infra/lambda/shared/github-gate.mjs 側(画面に分岐を持たせない)。
|
|
7695
|
+
// Backlog / Figma / Drive は実測そのものが新規なので、まだ出さない。
|
|
7266
7696
|
//
|
|
7267
7697
|
// **origin === "explicit" でも出す。** 確認は読むだけで、どの登録ファイル由来かに
|
|
7268
7698
|
// 関係なく効く(書き込みになる「表示名を更新する」だけが由来で出し分かれる)
|
|
7269
|
-
if (SITE.intake && s.type
|
|
7699
|
+
if (SITE.intake && probeTypeOf(s.type) && s.ref) {
|
|
7270
7700
|
var probe = probeSection(s, card, gateEl);
|
|
7271
7701
|
// **ボタンは行の中(「登録を解除」の隣)。** 独立した段を作ると1行が2段になる
|
|
7272
7702
|
card.appendChild(probe.btn);
|
|
@@ -7442,6 +7872,20 @@ ${nextStateDelay.toString()}
|
|
|
7442
7872
|
* その能力の行が応答から消え、**タイルの件数だけが行ったり来たりする**。
|
|
7443
7873
|
*/
|
|
7444
7874
|
var OPEN_CAPS = [];
|
|
7875
|
+
/**
|
|
7876
|
+
* 画面の能力名 → 接続状態ストアの能力名(対応表の実体は STATE_CAP_NAMES)。
|
|
7877
|
+
*
|
|
7878
|
+
* **「stateCapIdOf」(サーバへ「開いた」と伝える側)とは分ける。** あちらは
|
|
7879
|
+
* **登録ファイルを読ませる**要求なので、増やすと GitHub の枠を削る。こちらは
|
|
7880
|
+
* 届いた 「stateNow」 の行を引くだけで、**要求とは無関係に会議の行も届く**
|
|
7881
|
+
* (ストアの問い合わせは能力で絞られていない)
|
|
7882
|
+
*/
|
|
7883
|
+
function capIdOf(cap) {
|
|
7884
|
+
for (var sid in STATE_CAP_NAMES) {
|
|
7885
|
+
if (Object.prototype.hasOwnProperty.call(STATE_CAP_NAMES, sid) && STATE_CAP_NAMES[sid] === cap) return sid;
|
|
7886
|
+
}
|
|
7887
|
+
return "";
|
|
7888
|
+
}
|
|
7445
7889
|
/** 画面の能力名 → op=state の識別子(対応表の実体は REGISTERED_CAP_NAMES) */
|
|
7446
7890
|
function stateCapIdOf(cap) {
|
|
7447
7891
|
for (var id in REGISTERED_CAP_NAMES) {
|
|
@@ -7514,6 +7958,12 @@ ${nextStateDelay.toString()}
|
|
|
7514
7958
|
// ここより後に置くと**その案件だけ素通り**する
|
|
7515
7959
|
LIVE_REGISTERED = sanitizeRegisteredNow(j && j.registeredNow);
|
|
7516
7960
|
LIVE_STATE = sanitizeStateNow(j && j.stateNow);
|
|
7961
|
+
// 🔴 **鍵・能力・組織も fleet より先に読む(工程1.5)。** まとめ表を
|
|
7962
|
+
// まだ持たない案件は下で throw するので、ここより後に置くと
|
|
7963
|
+
// **立ち上げ直後の案件=この修正が一番効く案件**だけ素通りする
|
|
7964
|
+
LIVE_SECRETS = sanitizeSecretsNow(j && j.secretsNow);
|
|
7965
|
+
LIVE_CAPS = sanitizeCapabilitiesNow(j && j.capabilitiesNow);
|
|
7966
|
+
LIVE_GH_OWNERS = sanitizeGithubOwnersNow(j && j.registeredNow && j.registeredNow.githubOwners);
|
|
7517
7967
|
MERGED = null;
|
|
7518
7968
|
if (!j || !j.fleet) throw new Error("no fleet");
|
|
7519
7969
|
LIVE_FLEET = normalizeFleet(j.fleet);
|
|
@@ -7826,6 +8276,13 @@ ${nextStateDelay.toString()}
|
|
|
7826
8276
|
* @returns { known, src } src は取得元の文字列("yes" のときだけ意味を持つ)
|
|
7827
8277
|
*/
|
|
7828
8278
|
function secretStateOf(kind, stateSpace, spaceCount) {
|
|
8279
|
+
// 🔴 **正本(secretsNow)が答えられるならそれ(工程1.5・#227)。** まとめ表は夜間の
|
|
8280
|
+
// スナップショットなので、**鍵を保存した直後は前日の「未登録」を出し続ける**
|
|
8281
|
+
// ——入れた人は「保存できていない」と読んで貼り直す。正本は Secrets Manager を
|
|
8282
|
+
// 都度見た答えなので、次のポーリングで「登録済み」になる。
|
|
8283
|
+
// 答えられないとき(行が来ていない・unknown)だけ、下のまとめ表の判定へ落ちる
|
|
8284
|
+
var now = secretStateFromNow(LIVE_SECRETS, kind, kind === "backlog" ? stateSpace : "");
|
|
8285
|
+
if (now) return now;
|
|
7829
8286
|
var perKind = (fleetVal("secretSources") || {})[kind] || "";
|
|
7830
8287
|
var spaces = fleetVal("secretSpaces");
|
|
7831
8288
|
if (kind === "backlog" && stateSpace && spaces && typeof spaces === "object") {
|
|
@@ -7842,6 +8299,22 @@ ${nextStateDelay.toString()}
|
|
|
7842
8299
|
return { known: "unknown", src: "" };
|
|
7843
8300
|
}
|
|
7844
8301
|
|
|
8302
|
+
/**
|
|
8303
|
+
* タイルの「⚠ トークンが未設定です」を出さないでよいか(工程1.5・#227)。
|
|
8304
|
+
*
|
|
8305
|
+
* **タイルは能力に1枚しかない**ので、スペースごとの答えは持てない。まとめ表の
|
|
8306
|
+
* 「secretSources[kind]」(種別ごとの1つの値)と**同じ粒度**に畳んで、1本でも
|
|
8307
|
+
* 入っていれば注記を出さない——スペースごとの充足は行の中の鍵カードが答える。
|
|
8308
|
+
*
|
|
8309
|
+
* 正本が「確かめられなかった」(unknown)と答えたときだけ、まとめ表の値に落ちる。
|
|
8310
|
+
*/
|
|
8311
|
+
function secretPresentForTile(kind) {
|
|
8312
|
+
var now = secretRegisteredAnywhere(LIVE_SECRETS, kind);
|
|
8313
|
+
if (now === "yes") return true;
|
|
8314
|
+
if (now === "no") return false;
|
|
8315
|
+
return !!((fleetVal("secretSources") || {})[kind]);
|
|
8316
|
+
}
|
|
8317
|
+
|
|
7845
8318
|
/**
|
|
7846
8319
|
* @param opts { space, stateSpace, spaceCount, inRow }
|
|
7847
8320
|
* space … 保存時に名乗る Backlog スペース(名乗らないときは空)
|
package/package.json
CHANGED