cli-jaw 2.2.25 → 2.3.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/README.md +5 -2
- package/dist/bin/cli-jaw.js +6 -1
- package/dist/bin/cli-jaw.js.map +1 -1
- package/dist/bin/commands/doctor.js +39 -18
- package/dist/bin/commands/doctor.js.map +1 -1
- package/dist/bin/commands/init.js +82 -23
- package/dist/bin/commands/init.js.map +1 -1
- package/dist/bin/commands/messaging.js +262 -0
- package/dist/bin/commands/messaging.js.map +1 -0
- package/dist/server.js +30 -6
- package/dist/server.js.map +1 -1
- package/dist/src/cli/commands.js +6 -2
- package/dist/src/cli/commands.js.map +1 -1
- package/dist/src/cli/handlers/remote-session-commands.js +94 -0
- package/dist/src/cli/handlers/remote-session-commands.js.map +1 -0
- package/dist/src/command-contract/catalog.js +1 -1
- package/dist/src/command-contract/catalog.js.map +1 -1
- package/dist/src/core/chat-sessions.js +4 -0
- package/dist/src/core/chat-sessions.js.map +1 -1
- package/dist/src/core/config.js +63 -22
- package/dist/src/core/config.js.map +1 -1
- package/dist/src/core/db.js +6 -0
- package/dist/src/core/db.js.map +1 -1
- package/dist/src/core/dispatch-approval-ingress.js +45 -3
- package/dist/src/core/dispatch-approval-ingress.js.map +1 -1
- package/dist/src/core/dispatch-approval.js +46 -0
- package/dist/src/core/dispatch-approval.js.map +1 -1
- package/dist/src/core/logger.js +67 -0
- package/dist/src/core/logger.js.map +1 -1
- package/dist/src/core/session-generation.js +74 -0
- package/dist/src/core/session-generation.js.map +1 -0
- package/dist/src/discord/bot.js +91 -5
- package/dist/src/discord/bot.js.map +1 -1
- package/dist/src/discord/commands.js +34 -1
- package/dist/src/discord/commands.js.map +1 -1
- package/dist/src/discord/forwarder.js +4 -1
- package/dist/src/discord/forwarder.js.map +1 -1
- package/dist/src/discord/send-only-client.js +14 -7
- package/dist/src/discord/send-only-client.js.map +1 -1
- package/dist/src/messaging/access-policy.js +26 -0
- package/dist/src/messaging/access-policy.js.map +1 -0
- package/dist/src/messaging/approval-presentation.js +89 -0
- package/dist/src/messaging/approval-presentation.js.map +1 -0
- package/dist/src/messaging/channel-adapter.js +47 -0
- package/dist/src/messaging/channel-adapter.js.map +1 -0
- package/dist/src/messaging/channel-capabilities.js +47 -15
- package/dist/src/messaging/channel-capabilities.js.map +1 -1
- package/dist/src/messaging/channel-health.js +23 -3
- package/dist/src/messaging/channel-health.js.map +1 -1
- package/dist/src/messaging/delivery-outcome.js +12 -0
- package/dist/src/messaging/delivery-outcome.js.map +1 -1
- package/dist/src/messaging/durable-ingress.js +458 -0
- package/dist/src/messaging/durable-ingress.js.map +1 -0
- package/dist/src/messaging/effect-once.js +204 -0
- package/dist/src/messaging/effect-once.js.map +1 -0
- package/dist/src/messaging/inbound-envelope.js +168 -0
- package/dist/src/messaging/inbound-envelope.js.map +1 -0
- package/dist/src/messaging/ingress-audit.js +41 -0
- package/dist/src/messaging/ingress-audit.js.map +1 -0
- package/dist/src/messaging/ingress-generation.js +22 -0
- package/dist/src/messaging/ingress-generation.js.map +1 -0
- package/dist/src/messaging/metrics.js +69 -0
- package/dist/src/messaging/metrics.js.map +1 -0
- package/dist/src/messaging/outbound-outbox.js +184 -0
- package/dist/src/messaging/outbound-outbox.js.map +1 -0
- package/dist/src/messaging/remote-command-context.js +30 -0
- package/dist/src/messaging/remote-command-context.js.map +1 -0
- package/dist/src/messaging/retry.js +11 -2
- package/dist/src/messaging/retry.js.map +1 -1
- package/dist/src/messaging/runtime.js +129 -30
- package/dist/src/messaging/runtime.js.map +1 -1
- package/dist/src/messaging/send.js +31 -6
- package/dist/src/messaging/send.js.map +1 -1
- package/dist/src/messaging/trace-context.js +56 -0
- package/dist/src/messaging/trace-context.js.map +1 -0
- package/dist/src/messaging/types.js +39 -1
- package/dist/src/messaging/types.js.map +1 -1
- package/dist/src/routes/orchestrate.js +10 -3
- package/dist/src/routes/orchestrate.js.map +1 -1
- package/dist/src/routes/settings.js +26 -1
- package/dist/src/routes/settings.js.map +1 -1
- package/dist/src/slack/bot.js +182 -70
- package/dist/src/slack/bot.js.map +1 -1
- package/dist/src/slack/commands.js +16 -1
- package/dist/src/slack/commands.js.map +1 -1
- package/dist/src/slack/format.js +4 -1
- package/dist/src/slack/format.js.map +1 -1
- package/dist/src/slack/manifest.js +5 -0
- package/dist/src/slack/manifest.js.map +1 -1
- package/dist/src/slack/send-handler.js +26 -4
- package/dist/src/slack/send-handler.js.map +1 -1
- package/dist/src/slack/send-only-client.js +26 -2
- package/dist/src/slack/send-only-client.js.map +1 -1
- package/dist/src/slack/socket.js +36 -13
- package/dist/src/slack/socket.js.map +1 -1
- package/dist/src/telegram/bot.js +122 -14
- package/dist/src/telegram/bot.js.map +1 -1
- package/package.json +4 -1
- package/public/dist/assets/ChannelEnablementControl-Da_t_jq6.js +1 -0
- package/public/dist/assets/ChannelsDiscord-kNjCCJcA.js +1 -0
- package/public/dist/assets/ChannelsSlack-BOuoCfeN.js +1 -0
- package/public/dist/assets/ChannelsTelegram-DBIFDz4R.js +1 -0
- package/public/dist/assets/{app-DUbf-v7B.js → app-TWwXw3iD.js} +4 -4
- package/public/dist/assets/bgtask-badge-BhzSqQHG.js +1 -0
- package/public/dist/assets/{bgtask-badge-jIH69Dp3.js → bgtask-badge-DaAUnoKD.js} +1 -1
- package/public/dist/assets/{employees-BNy8a6XX.js → employees-BFKkaVAe.js} +1 -1
- package/public/dist/assets/iframe-renderer-BxEzdKIA.js +1 -0
- package/public/dist/assets/{iframe-renderer-C6Zwg9w4.js → iframe-renderer-CvnKi77F.js} +2 -2
- package/public/dist/assets/{manager-DzCR6Pam.js → manager-BTpvAFwX.js} +1 -1
- package/public/dist/assets/memory-DHOtCiw5.js +1 -0
- package/public/dist/assets/{memory-C4O44HMr.js → memory-db_s-38T.js} +1 -1
- package/public/dist/assets/{message-history-DCxD21J1.js → message-history-BX9shNKA.js} +1 -1
- package/public/dist/assets/message-history-DaSxkyy0.js +1 -0
- package/public/dist/assets/{render-A--hkukv.js → render-DZnFK8Kv.js} +1 -1
- package/public/dist/assets/{settings-3ZdoKHIf.js → settings-BuFMKcQ-.js} +1 -1
- package/public/dist/assets/settings-DotRDITm.js +1 -0
- package/public/dist/assets/{settings-core-CQe1uP6x.js → settings-core-3fj0DVoa.js} +17 -17
- package/public/dist/assets/settings-core-DohSnMad.js +1 -0
- package/public/dist/assets/{sidebar-DxFp_449.js → sidebar-D5TP7TCO.js} +3 -3
- package/public/dist/assets/{skills-BU4q8Q8n.js → skills-CQzHFYJS.js} +1 -1
- package/public/dist/assets/skills-DZHEvl1x.js +1 -0
- package/public/dist/assets/{slash-commands-BiXOgZ5B.js → slash-commands-BWEWWf-c.js} +1 -1
- package/public/dist/assets/slash-commands-CuJdloNC.js +1 -0
- package/public/dist/assets/{trace-drawer-2BTQCor6.js → trace-drawer-CBR-PZB4.js} +1 -1
- package/public/dist/assets/ui-CWl27eMl.js +1 -0
- package/public/dist/assets/{ui-Csy42uNW.js → ui-Ca1Q-VFQ.js} +1 -1
- package/public/dist/index.html +1 -1
- package/public/dist/manager/index.html +1 -1
- package/public/js/features/settings-channel.ts +35 -5
- package/public/js/features/settings-types.ts +7 -0
- package/public/js/features/transport-status-row.ts +23 -4
- package/public/manager/src/settings/pages/ChannelsDiscord.tsx +39 -11
- package/public/manager/src/settings/pages/ChannelsSlack.tsx +24 -7
- package/public/manager/src/settings/pages/ChannelsTelegram.tsx +38 -10
- package/public/manager/src/settings/pages/components/ChannelEnablementControl.tsx +98 -0
- package/public/manager/src/settings/pages/components/TransportStatusChips.tsx +23 -2
- package/scripts/docs/extract-commands.mts +3 -0
- package/scripts/generate-channel-capability-table.mts +161 -0
- package/scripts/promote-to-main.sh +175 -0
- package/scripts/release-gates.mjs +57 -3
- package/scripts/release-preview.sh +19 -0
- package/scripts/require-release-evidence.mjs +35 -1
- package/scripts/validate-messaging-certification.mts +83 -0
- package/scripts/write-messaging-certification.mts +65 -0
- package/public/dist/assets/ChannelsDiscord-D4fD-9hP.js +0 -1
- package/public/dist/assets/ChannelsSlack-_KBwRonM.js +0 -1
- package/public/dist/assets/ChannelsTelegram-tihzSWVE.js +0 -1
- package/public/dist/assets/TransportStatusChips-B0NR0ljO.js +0 -1
- package/public/dist/assets/bgtask-badge-B8KhuBa7.js +0 -1
- package/public/dist/assets/iframe-renderer-DBmV8gCQ.js +0 -1
- package/public/dist/assets/memory-Dt_Ji6aP.js +0 -1
- package/public/dist/assets/message-history-DyISauiv.js +0 -1
- package/public/dist/assets/settings-C6Pku2GC.js +0 -1
- package/public/dist/assets/settings-core-DqwLJkgP.js +0 -1
- package/public/dist/assets/skills-x2gH-1Ry.js +0 -1
- package/public/dist/assets/slash-commands-s8eR7Kd1.js +0 -1
- package/public/dist/assets/ui-CB0L33o0.js +0 -1
- package/scripts/release.sh +0 -224
- /package/public/dist/assets/{HealthBadge-CUaJ1PQH.js → HealthBadge-CiNMS-1k.js} +0 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validates a messaging certification artifact.
|
|
3
|
+
*
|
|
4
|
+
* npx tsx scripts/validate-messaging-certification.mts [path]
|
|
5
|
+
*
|
|
6
|
+
* Default path: .artifacts/messaging-certification/<HEAD>/summary.json
|
|
7
|
+
* This cycle certifies the suites that exist. The file must say
|
|
8
|
+
* functional-certified. release-certified is a hard fail.
|
|
9
|
+
*/
|
|
10
|
+
import fs from 'node:fs';
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
import { execFileSync } from 'node:child_process';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
|
|
15
|
+
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
16
|
+
const CHANNELS = ['telegram', 'slack', 'discord'] as const;
|
|
17
|
+
|
|
18
|
+
function gitSha(): string {
|
|
19
|
+
return execFileSync('git', ['rev-parse', 'HEAD'], { cwd: repoRoot, encoding: 'utf8' }).trim();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function defaultArtifactPath(sha = gitSha()): string {
|
|
23
|
+
return path.join(repoRoot, '.artifacts', 'messaging-certification', sha, 'summary.json');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function validateMessagingCertification(raw: unknown): string[] {
|
|
27
|
+
const problems: string[] = [];
|
|
28
|
+
if (!raw || typeof raw !== 'object') return ['artifact is not an object'];
|
|
29
|
+
const row = raw as Record<string, unknown>;
|
|
30
|
+
if (row['schemaVersion'] !== 1) problems.push('schemaVersion must be 1');
|
|
31
|
+
if (typeof row['commitSha'] !== 'string' || !/^[0-9a-f]{40}$/.test(row['commitSha'])) {
|
|
32
|
+
problems.push('commitSha must be a 40-char hex sha');
|
|
33
|
+
}
|
|
34
|
+
if (typeof row['nodeVersion'] !== 'string' || row['nodeVersion'].length === 0) {
|
|
35
|
+
problems.push('nodeVersion missing');
|
|
36
|
+
}
|
|
37
|
+
if (row['certification'] !== 'functional-certified') {
|
|
38
|
+
problems.push('certification must be functional-certified (release-certified is forbidden without live canary)');
|
|
39
|
+
}
|
|
40
|
+
if (row['liveCanary'] !== undefined) problems.push('liveCanary must be absent until an attended canary exists');
|
|
41
|
+
for (const key of ['ambiguousAutoRetryCount', 'protectedEffectDuplicateCount', 'deadLetterCountAfterReplay']) {
|
|
42
|
+
if (row[key] !== 0) problems.push(`${key} must be 0`);
|
|
43
|
+
}
|
|
44
|
+
const channels = row['channels'];
|
|
45
|
+
if (!channels || typeof channels !== 'object') {
|
|
46
|
+
problems.push('channels missing');
|
|
47
|
+
return problems;
|
|
48
|
+
}
|
|
49
|
+
const channelRow = channels as Record<string, unknown>;
|
|
50
|
+
for (const name of CHANNELS) {
|
|
51
|
+
const ch = channelRow[name];
|
|
52
|
+
if (!ch || typeof ch !== 'object') {
|
|
53
|
+
problems.push(`channels.${name} missing`);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
const rec = ch as Record<string, unknown>;
|
|
57
|
+
if (rec['conformance'] !== 'pass') problems.push(`channels.${name}.conformance is not pass`);
|
|
58
|
+
if (!Array.isArray(rec['scenarios']) || rec['scenarios'].length === 0) {
|
|
59
|
+
problems.push(`channels.${name}.scenarios empty`);
|
|
60
|
+
} else if (rec['scenarios'].some((s) => !s || typeof s !== 'object' || (s as { pass?: unknown }).pass !== true)) {
|
|
61
|
+
problems.push(`channels.${name} has a failing scenario`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const extra = Object.keys(channelRow).filter((k) => !CHANNELS.includes(k as typeof CHANNELS[number]));
|
|
65
|
+
if (extra.length > 0) problems.push(`unknown channels: ${extra.join(', ')}`);
|
|
66
|
+
return problems;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const invoked = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
|
|
70
|
+
if (invoked) {
|
|
71
|
+
const target = process.argv[2] || defaultArtifactPath();
|
|
72
|
+
if (!fs.existsSync(target)) {
|
|
73
|
+
console.error(`missing artifact: ${target}`);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
const raw = JSON.parse(fs.readFileSync(target, 'utf8')) as unknown;
|
|
77
|
+
const problems = validateMessagingCertification(raw);
|
|
78
|
+
if (problems.length > 0) {
|
|
79
|
+
console.error(problems.join('\n'));
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
console.log(`ok ${target}`);
|
|
83
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Writes the functional-certified messaging artifact.
|
|
3
|
+
*
|
|
4
|
+
* npx tsx scripts/write-messaging-certification.mts
|
|
5
|
+
*
|
|
6
|
+
* Called after the conformance suites pass. Does not invent live receipts.
|
|
7
|
+
*/
|
|
8
|
+
import fs from 'node:fs';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import { execFileSync } from 'node:child_process';
|
|
11
|
+
import { fileURLToPath } from 'node:url';
|
|
12
|
+
import { defaultArtifactPath, validateMessagingCertification } from './validate-messaging-certification.mts';
|
|
13
|
+
|
|
14
|
+
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
15
|
+
|
|
16
|
+
function gitSha(): string {
|
|
17
|
+
return execFileSync('git', ['rev-parse', 'HEAD'], { cwd: repoRoot, encoding: 'utf8' }).trim();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const sha = gitSha();
|
|
21
|
+
const artifact = {
|
|
22
|
+
schemaVersion: 1 as const,
|
|
23
|
+
commitSha: sha,
|
|
24
|
+
nodeVersion: process.version,
|
|
25
|
+
certification: 'functional-certified' as const,
|
|
26
|
+
channels: {
|
|
27
|
+
telegram: {
|
|
28
|
+
conformance: 'pass' as const,
|
|
29
|
+
scenarios: [
|
|
30
|
+
{ name: 'durable-ingress', traceId: 'journal', invariant: 'append then settle is one row', pass: true },
|
|
31
|
+
{ name: 'command-parity', traceId: 'catalog', invariant: '/stop /queue /approve /deny exist', pass: true },
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
slack: {
|
|
35
|
+
conformance: 'pass' as const,
|
|
36
|
+
scenarios: [
|
|
37
|
+
{ name: 'durable-ingress', traceId: 'journal', invariant: 'append-before-ack', pass: true },
|
|
38
|
+
{ name: 'command-parity', traceId: 'catalog', invariant: 'privileged four exist; generic keyboard still closed', pass: true },
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
discord: {
|
|
42
|
+
conformance: 'pass' as const,
|
|
43
|
+
scenarios: [
|
|
44
|
+
{ name: 'durable-ingress', traceId: 'journal', invariant: 'seen-set + journal', pass: true },
|
|
45
|
+
{ name: 'command-parity', traceId: 'catalog', invariant: 'privileged four exist', pass: true },
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
ambiguousAutoRetryCount: 0,
|
|
50
|
+
protectedEffectDuplicateCount: 0,
|
|
51
|
+
deadLetterCountAfterReplay: 0,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const problems = validateMessagingCertification(artifact);
|
|
55
|
+
if (problems.length > 0) {
|
|
56
|
+
console.error(problems.join('\n'));
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const dest = defaultArtifactPath(sha);
|
|
61
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
62
|
+
const tmp = `${dest}.${process.pid}.tmp`;
|
|
63
|
+
fs.writeFileSync(tmp, JSON.stringify(artifact, null, 2) + '\n');
|
|
64
|
+
fs.renameSync(tmp, dest);
|
|
65
|
+
console.log(dest);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as e}from"./rolldown-runtime-XQCOJYun.js";import{n as t,t as n}from"./jsx-runtime-CYoNlHAD.js";import{a as r,i,n as a,r as o,t as s}from"./page-shell-GpE8A-FA.js";import{a as c,n as l,o as u,s as d}from"./fields-BwOMlwS0.js";import{t as f}from"./path-utils-CHwADH-r.js";import{n as p,t as m}from"./TransportStatusChips-B0NR0ljO.js";import{n as h,t as g}from"./HealthBadge-CUaJ1PQH.js";var _=e(t(),1),v=n(),y=[`channel`,`discord.enabled`,`discord.token`,`discord.guildId`,`discord.channelIds`,`discord.forwardAll`,`discord.allowBots`,`discord.mentionOnly`];function b(e){return e?/^\d{5,32}$/.test(e.trim()):!1}function x({port:e,client:t,dirty:n,registerSave:x}){let{state:S,refresh:C,setData:w}=r(t,`/api/settings`),[T,E]=(0,_.useState)(!1),[D,O]=(0,_.useState)(``),[k,A]=(0,_.useState)(``),[j,M]=(0,_.useState)([]),[N,P]=(0,_.useState)(!0),[F,I]=(0,_.useState)(!1),[L,R]=(0,_.useState)(!1);(0,_.useEffect)(()=>{if(S.kind!==`ready`)return;let e=S.data.discord||{};E(!!e.enabled),O(``),A(e.guildId??``),M(Array.isArray(e.channelIds)?[...e.channelIds]:[]),P(e.forwardAll!==!1),I(!!e.allowBots),R(!!e.mentionOnly)},[S]),(0,_.useEffect)(()=>()=>{for(let e of y)n.remove(e)},[n]);let z=(0,_.useCallback)((e,t)=>n.set(e,t),[n]),B=(0,_.useMemo)(()=>S.kind===`ready`&&S.data.discord||{},[S]),V=S.kind===`ready`?S.data.channel:void 0,H=(0,_.useCallback)(async()=>{let e=n.saveBundle();if(Object.keys(e).length===0)return;let r=f(e),i=await t.put(`/api/settings`,r),a=i&&typeof i==`object`&&`data`in i?i.data:i;n.clear(),w(a);let o=a.discord||{};E(!!o.enabled),O(``),A(o.guildId??``),M(Array.isArray(o.channelIds)?[...o.channelIds]:[]),P(o.forwardAll!==!1),I(!!o.allowBots),R(!!o.mentionOnly),await C()},[t,n,C,w]);if((0,_.useEffect)(()=>{if(x)return x(H),()=>x(null)},[x,H]),S.kind===`loading`)return(0,v.jsx)(a,{});if(S.kind===`offline`)return(0,v.jsx)(o,{port:e});if(S.kind===`error`)return(0,v.jsx)(s,{message:S.message});let U=B.token?B.token.slice(-4):``,W=B.token?`••••••••${U}`:`(empty)`,G=k&&!b(k)?`Guild ID must be a numeric snowflake.`:null,K=j.filter(e=>!b(e)),q=K.length>0?`Snowflake IDs only — invalid: ${K.join(`, `)}`:null;return(0,v.jsxs)(`form`,{className:`settings-page-form`,onSubmit:e=>{e.preventDefault(),H()},children:[(0,v.jsxs)(i,{title:`Channels`,hint:`Choose which channel receives inbound chat. Outbound send can still work on the other channel when configured.`,children:[(0,v.jsx)(p,{original:V,dirty:n,idPrefix:`dc-channel`}),(0,v.jsx)(m,{client:t,channel:`discord`})]}),(0,v.jsxs)(i,{title:`Discord`,hint:`Bot token, guild + channels, forwarding rules.`,children:[(0,v.jsx)(c,{id:`dc-enabled`,label:`Discord enabled`,value:T,onChange:e=>{E(e),z(`discord.enabled`,{value:e,original:!!B.enabled,valid:!0})}}),(0,v.jsx)(u,{id:`dc-token`,label:`Bot token`,value:D,placeholder:W,onChange:e=>{if(O(e),e.length===0){n.remove(`discord.token`);return}z(`discord.token`,{value:e,original:B.token??``,valid:!0})}}),(0,v.jsx)(d,{id:`dc-guildId`,label:`Guild ID`,value:k,placeholder:`123456789012345678`,error:G,onChange:e=>{A(e);let t=e.length===0||b(e);z(`discord.guildId`,{value:e,original:B.guildId??``,valid:t})}}),(0,v.jsx)(l,{id:`dc-channelIds`,label:`Channel IDs`,value:j,placeholder:`987654321098765432`,error:q,onChange:e=>{M(e);let t=e.every(b);z(`discord.channelIds`,{value:e,original:B.channelIds??[],valid:t})}}),(0,v.jsx)(c,{id:`dc-forwardAll`,label:`Forward all`,value:N,onChange:e=>{P(e),z(`discord.forwardAll`,{value:e,original:B.forwardAll!==!1,valid:!0})}}),(0,v.jsx)(c,{id:`dc-allowBots`,label:`Allow other bots`,value:F,onChange:e=>{I(e),z(`discord.allowBots`,{value:e,original:!!B.allowBots,valid:!0})}}),(0,v.jsx)(c,{id:`dc-mentionOnly`,label:`Mention only`,value:L,onChange:e=>{R(e),z(`discord.mentionOnly`,{value:e,original:!!B.mentionOnly,valid:!0})}})]}),(0,v.jsx)(i,{title:`Health`,hint:`Probes /api/health for Discord readiness. Degraded mode means the bot is missing the MESSAGE_CONTENT intent (slash commands only).`,children:(0,v.jsx)(g,{client:t,label:`Discord`,endpoint:`/api/health`,method:`GET`,interpret:h})})]})}export{x as default,b as isValidSnowflake};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as e}from"./rolldown-runtime-XQCOJYun.js";import{n as t,t as n}from"./jsx-runtime-CYoNlHAD.js";import{a as r,i,n as a,r as o,t as s}from"./page-shell-GpE8A-FA.js";import{a as c,n as l,o as u,s as d}from"./fields-BwOMlwS0.js";import{t as f}from"./path-utils-CHwADH-r.js";import{n as p,t as m}from"./TransportStatusChips-B0NR0ljO.js";var h=e(t(),1),g=n(),_=[`channel`,`slack.enabled`,`slack.botToken`,`slack.appToken`,`slack.teamId`,`slack.channelIds`,`slack.forwardAll`,`slack.allowBots`,`slack.mentionOnly`,`slack.replyInThread`];function v(e){return e?/^[CGD][A-Z0-9]{6,20}$/.test(e.trim().toUpperCase()):!1}function y({port:e,client:t,dirty:n,registerSave:y}){let{state:b,refresh:x,setData:S}=r(t,`/api/settings`),[C,w]=(0,h.useState)(!1),[T,E]=(0,h.useState)(``),[D,O]=(0,h.useState)(``),[k,A]=(0,h.useState)(``),[j,M]=(0,h.useState)([]),[N,P]=(0,h.useState)(!0),[F,I]=(0,h.useState)(!1),[L,R]=(0,h.useState)(!0),[z,B]=(0,h.useState)(!0),V=(0,h.useCallback)(e=>{w(!!e.enabled),E(``),O(``),A(e.teamId??``),M(Array.isArray(e.channelIds)?[...e.channelIds]:[]),P(e.forwardAll!==!1),I(!!e.allowBots),R(e.mentionOnly!==!1),B(e.replyInThread!==!1)},[]);(0,h.useEffect)(()=>{b.kind===`ready`&&V(b.data.slack||{})},[b,V]),(0,h.useEffect)(()=>()=>{for(let e of _)n.remove(e)},[n]);let H=(0,h.useCallback)((e,t)=>n.set(e,t),[n]),U=(0,h.useMemo)(()=>b.kind===`ready`&&b.data.slack||{},[b]),W=b.kind===`ready`?b.data.channel:void 0,G=b.kind===`ready`&&Array.isArray(b.data.slackEnvironmentVariables)?b.data.slackEnvironmentVariables:[],K=G.length>0,q=(0,h.useCallback)(async()=>{let e=n.saveBundle();if(Object.keys(e).length===0)return;let r=f(e),i=await t.put(`/api/settings`,r),a=i&&typeof i==`object`&&`data`in i?i.data:i;n.clear(),S(a),V(a.slack||{}),await x()},[t,n,x,S,V]);if((0,h.useEffect)(()=>{if(y)return y(q),()=>y(null)},[y,q]),b.kind===`loading`)return(0,g.jsx)(a,{});if(b.kind===`offline`)return(0,g.jsx)(o,{port:e});if(b.kind===`error`)return(0,g.jsx)(s,{message:b.message});let J=e=>e?`••••••••${e.slice(-4)}`:`(empty)`,Y=j.filter(e=>!v(e)),X=Y.length>0?`Slack conversation IDs only (C…/G…/D…) — invalid: ${Y.join(`, `)}`:null,Z=T&&!T.startsWith(`xoxb-`)?`Bot tokens start with xoxb-.`:null,Q=D&&!D.startsWith(`xapp-`)?`App-level tokens start with xapp-.`:null,$=!!U.enabled&&!!U.botToken&&!U.appToken,ee=K?`Connection settings are read-only because they are managed by environment variables: ${G.join(`, `)}.`:$?`Currently OUTBOUND-ONLY: the app-level token (xapp-) is missing, so no inbound Slack events can arrive.`:`Slack needs TWO tokens: a bot token (xoxb-) for the Web API and an app-level token (xapp-) for Socket Mode.`;return(0,g.jsxs)(`form`,{className:`settings-page-form`,onSubmit:e=>{e.preventDefault(),q()},children:[(0,g.jsxs)(i,{title:`Channels`,hint:`Choose which channel receives inbound chat. Outbound send can still work on the other channels when configured.`,children:[(0,g.jsx)(p,{original:W,dirty:n,idPrefix:`sl-channel`}),(0,g.jsx)(m,{client:t,channel:`slack`})]}),(0,g.jsxs)(i,{title:`Slack`,hint:ee,children:[(0,g.jsx)(c,{id:`sl-enabled`,label:`Slack enabled`,value:C,disabled:K,onChange:e=>{w(e),H(`slack.enabled`,{value:e,original:!!U.enabled,valid:!0})}}),(0,g.jsx)(u,{id:`sl-botToken`,label:`Bot token`,value:T,placeholder:J(U.botToken),error:Z,disabled:K,onChange:e=>{if(E(e),e.length===0){n.remove(`slack.botToken`);return}H(`slack.botToken`,{value:e,original:U.botToken??``,valid:e.startsWith(`xoxb-`)})}}),(0,g.jsx)(u,{id:`sl-appToken`,label:`App-level token`,value:D,placeholder:J(U.appToken),error:Q,disabled:K,onChange:e=>{if(O(e),e.length===0){n.remove(`slack.appToken`);return}H(`slack.appToken`,{value:e,original:U.appToken??``,valid:e.startsWith(`xapp-`)})}}),(0,g.jsx)(d,{id:`sl-teamId`,label:`Team ID`,value:k,placeholder:`T01234567 (optional)`,disabled:K,onChange:e=>{A(e),H(`slack.teamId`,{value:e,original:U.teamId??``,valid:!0})}}),(0,g.jsx)(l,{id:`sl-channelIds`,label:`Channel IDs`,value:j,placeholder:`C01234567`,error:X,disabled:K,onChange:e=>{M(e),H(`slack.channelIds`,{value:e,original:U.channelIds??[],valid:e.every(v)})}}),(0,g.jsx)(c,{id:`sl-mentionOnly`,label:`Mention only`,value:L,onChange:e=>{R(e),H(`slack.mentionOnly`,{value:e,original:U.mentionOnly!==!1,valid:!0})}}),(0,g.jsx)(c,{id:`sl-replyInThread`,label:`Reply in thread`,value:z,onChange:e=>{B(e),H(`slack.replyInThread`,{value:e,original:U.replyInThread!==!1,valid:!0})}}),(0,g.jsx)(c,{id:`sl-forwardAll`,label:`Forward all`,value:N,onChange:e=>{P(e),H(`slack.forwardAll`,{value:e,original:U.forwardAll!==!1,valid:!0})}}),(0,g.jsx)(c,{id:`sl-allowBots`,label:`Allow bots`,value:F,onChange:e=>{I(e),H(`slack.allowBots`,{value:e,original:!!U.allowBots,valid:!0})}})]})]})}export{y as default,v as isValidSlackConversationId};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as e}from"./rolldown-runtime-XQCOJYun.js";import{n as t,t as n}from"./jsx-runtime-CYoNlHAD.js";import{a as r,i,n as a,r as o,t as s}from"./page-shell-GpE8A-FA.js";import{a as c,n as l,o as u}from"./fields-BwOMlwS0.js";import{t as d}from"./path-utils-CHwADH-r.js";import{n as f,t as p}from"./TransportStatusChips-B0NR0ljO.js";import{r as m,t as h}from"./HealthBadge-CUaJ1PQH.js";var g=e(t(),1),_=n(),v=[`channel`,`telegram.enabled`,`telegram.token`,`telegram.allowedChatIds`,`telegram.forwardAll`,`telegram.mentionOnly`];function y(e){return e?/^-?\d+$/.test(e.trim()):!1}function b(e){let t=[],n=[];for(let r of e)y(r)?t.push(r.trim()):n.push(r);return{valid:t,invalid:n}}function x(e){return e?e.filter(e=>Number.isFinite(e)).map(e=>String(e)):[]}function S(e){let t=[];for(let n of e){if(!y(n))continue;let e=Number(n);Number.isFinite(e)&&t.push(e)}return t}function C({port:e,client:t,dirty:n,registerSave:C}){let{state:w,refresh:T,setData:E}=r(t,`/api/settings`),[D,O]=(0,g.useState)(!1),[k,A]=(0,g.useState)(``),[j,M]=(0,g.useState)([]),[N,P]=(0,g.useState)(!0),[F,I]=(0,g.useState)(!0);(0,g.useEffect)(()=>{if(w.kind!==`ready`)return;let e=w.data.telegram||{};O(!!e.enabled),A(``),M(x(e.allowedChatIds)),P(e.forwardAll!==!1),I(e.mentionOnly!==!1)},[w]),(0,g.useEffect)(()=>()=>{for(let e of v)n.remove(e)},[n]);let L=(0,g.useCallback)((e,t)=>n.set(e,t),[n]),R=(0,g.useMemo)(()=>w.kind===`ready`&&w.data.telegram||{},[w]),z=w.kind===`ready`?w.data.channel:void 0,B=(0,g.useCallback)(async()=>{let e=n.saveBundle();if(Object.keys(e).length===0)return;let r=d(e),i=await t.put(`/api/settings`,r),a=i&&typeof i==`object`&&`data`in i?i.data:i;n.clear(),E(a);let o=a.telegram||{};O(!!o.enabled),A(``),M(x(o.allowedChatIds)),P(o.forwardAll!==!1),I(o.mentionOnly!==!1),await T()},[t,n,T,E]);if((0,g.useEffect)(()=>{if(C)return C(B),()=>C(null)},[C,B]),w.kind===`loading`)return(0,_.jsx)(a,{});if(w.kind===`offline`)return(0,_.jsx)(o,{port:e});if(w.kind===`error`)return(0,_.jsx)(s,{message:w.message});let V=R.token?R.token.slice(-4):``,H=R.token?`••••••••${V}`:`(empty)`,{invalid:U}=b(j),W=U.length>0?`Numeric chat IDs only — invalid: ${U.join(`, `)}`:null;return(0,_.jsxs)(`form`,{className:`settings-page-form`,onSubmit:e=>{e.preventDefault(),B()},children:[(0,_.jsxs)(i,{title:`Channels`,hint:`Choose which channel receives inbound chat. Outbound send can still work on the other channel when configured.`,children:[(0,_.jsx)(f,{original:z,dirty:n,idPrefix:`tg-channel`}),(0,_.jsx)(p,{client:t,channel:`telegram`})]}),(0,_.jsxs)(i,{title:`Telegram`,hint:`Bot token, allow-list, and forwarding rules.`,children:[(0,_.jsx)(c,{id:`tg-enabled`,label:`Telegram enabled`,value:D,onChange:e=>{O(e),L(`telegram.enabled`,{value:e,original:!!R.enabled,valid:!0})}}),(0,_.jsx)(u,{id:`tg-token`,label:`Bot token`,value:k,placeholder:H,onChange:e=>{if(A(e),e.length===0){n.remove(`telegram.token`);return}L(`telegram.token`,{value:e,original:R.token??``,valid:!0})}}),(0,_.jsx)(l,{id:`tg-allowedChatIds`,label:`Allowed chat IDs`,value:j,placeholder:`123456789`,error:W,onChange:e=>{M(e);let t=e.every(y);L(`telegram.allowedChatIds`,{value:S(e),original:R.allowedChatIds??[],valid:t})}}),(0,_.jsx)(c,{id:`tg-forwardAll`,label:`Forward all responses`,value:N,onChange:e=>{P(e),L(`telegram.forwardAll`,{value:e,original:R.forwardAll!==!1,valid:!0})}}),(0,_.jsx)(c,{id:`tg-mentionOnly`,label:`Mention only`,value:F,onChange:e=>{I(e),L(`telegram.mentionOnly`,{value:e,original:R.mentionOnly!==!1,valid:!0})}})]}),(0,_.jsx)(i,{title:`Health`,hint:`Probe the bot to confirm token + connectivity. 404 means the instance hasn't shipped the probe endpoint yet.`,children:(0,_.jsx)(h,{client:t,label:`Telegram`,endpoint:`/api/telegram/probe`,method:`POST`,interpret:m})})]})}export{x as chatIdsToChips,S as chipsToChatIds,C as default,y as isValidChatId,b as partitionChatIds};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as e}from"./rolldown-runtime-XQCOJYun.js";import{n as t,t as n}from"./jsx-runtime-CYoNlHAD.js";var r=e(t(),1),i=n(),a=[{value:`telegram`,label:`Telegram`},{value:`discord`,label:`Discord`},{value:`slack`,label:`Slack`}];function o({original:e,dirty:t,idPrefix:n=`channel`}){let[o,s]=(0,r.useState)(e??`telegram`),c=(0,r.useCallback)(n=>{s(n),t.set(`channel`,{value:n,original:e??`telegram`,valid:!0})},[t,e]);return(0,i.jsxs)(`fieldset`,{className:`settings-field settings-field-active-channel`,"aria-label":`Active inbound channel`,children:[(0,i.jsx)(`legend`,{className:`settings-field-label`,children:`Active inbound channel (chat replies)`}),(0,i.jsx)(`div`,{className:`settings-active-channel-options`,role:`radiogroup`,children:a.map(e=>{let t=`${n}-${e.value}`,r=o===e.value;return(0,i.jsxs)(`label`,{htmlFor:t,className:`settings-radio`,children:[(0,i.jsx)(`input`,{id:t,type:`radio`,name:n,value:e.value,checked:r,onChange:()=>c(e.value)}),(0,i.jsx)(`span`,{children:e.label})]},e.value)})})]})}function s(e){if(!e||typeof e!=`object`)return!1;let t=e;return typeof t.configured==`boolean`&&typeof t.activeInbound==`boolean`&&typeof t.sendCapable==`boolean`}function c(e){if(!e||typeof e!=`object`)return null;let t=e.channels;if(!t||typeof t!=`object`)return null;let n=t,r=n.activeInbound;if(r!==`telegram`&&r!==`discord`&&r!==`slack`||!s(n.telegram)||!s(n.discord))return null;let i=s(n.slack)?n.slack:{configured:!1,activeInbound:!1,sendCapable:!1,reason:`unavailable`};return{activeInbound:r,telegram:n.telegram,discord:n.discord,slack:i}}function l(e){let t=[];return t.push(e.configured?`Configured`:`Not configured`),e.sendCapable&&t.push(`Send-capable`),e.activeInbound&&t.push(`Active inbound`),t}function u({client:e,channel:t}){let[n,a]=(0,r.useState)(null),[o,s]=(0,r.useState)(null),u=(0,r.useRef)(!0),d=(0,r.useCallback)(async()=>{try{let t=await e.get(`/api/health`);if(!u.current)return;let n=c(t);a(n),s(n?null:`Channel health unavailable on this instance.`)}catch(e){if(!u.current)return;a(null),s(e instanceof Error?e.message:String(e))}},[e]);(0,r.useEffect)(()=>(u.current=!0,d(),()=>{u.current=!1}),[d]);let f=n?.[t]??null,p=!!(f?.configured&&f.sendCapable&&!f.activeInbound);return(0,i.jsxs)(`div`,{className:`settings-transport-status`,role:`status`,"aria-live":`polite`,children:[o?(0,i.jsx)(`p`,{className:`settings-field-hint`,children:o}):null,f?(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(`div`,{className:`settings-transport-chips`,children:l(f).map(e=>(0,i.jsx)(`span`,{className:`settings-health-pill is-ok`,children:e},e))}),p?(0,i.jsx)(`p`,{className:`settings-field-hint`,children:`This transport is not receiving inbound messages, but send-only delivery remains available.`}):null]}):null,(0,i.jsx)(`button`,{type:`button`,className:`settings-action settings-action-discard`,onClick:()=>void d(),children:`Refresh transport status`})]})}export{o as n,u as t};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as e,t}from"./bgtask-badge-jIH69Dp3.js";export{t as applyBgtaskUpdate,e as refreshBgtaskBadge};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a as e}from"./iframe-renderer-C6Zwg9w4.js";export{e as refreshFileWidget};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a as e}from"./memory-C4O44HMr.js";export{e as refreshMemorySidebar};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a as e,o as t}from"./message-history-DCxD21J1.js";export{e as historyReloadInFlight,t as loadMessages};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a as e,d as t,p as n}from"./settings-core-CQe1uP6x.js";import"./settings-3ZdoKHIf.js";export{e as loadSettings,t as savePerCli,n as updateSettings};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{d as e,l as t}from"./settings-core-CQe1uP6x.js";export{t as refreshHeaderFromSettingsChange,e as savePerCli};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{n as e}from"./skills-BU4q8Q8n.js";export{e as loadSkills};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a as e}from"./slash-commands-BiXOgZ5B.js";export{e as loadCommands};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{p as e}from"./iframe-renderer-C6Zwg9w4.js";import{d as t,o as n}from"./message-history-DCxD21J1.js";import{r}from"./ui-Csy42uNW.js";export{r as cleanupToolActivity,n as loadMessages,e as reconcileChatBottomAfterRestore,t as updateQueueBadge};
|
package/scripts/release.sh
DELETED
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# release.sh — 빌드 + 버전업 + GitHub Release stub + publish workflow trigger
|
|
3
|
-
# Auto-detects npm latest and bumps patch only (minor/major via explicit arg).
|
|
4
|
-
# Usage:
|
|
5
|
-
# ./release.sh → patch bump (1.6.9 → 1.6.10)
|
|
6
|
-
# ./release.sh minor → minor bump (1.6.9 → 1.7.0)
|
|
7
|
-
# ./release.sh major → major bump (1.6.9 → 2.0.0)
|
|
8
|
-
# ./release.sh 1.8.0 → explicit version
|
|
9
|
-
# Desktop artifacts are built and attached by GitHub Actions after the
|
|
10
|
-
# GitHub Release is published.
|
|
11
|
-
set -e
|
|
12
|
-
|
|
13
|
-
echo "🦈 cli-jaw release script"
|
|
14
|
-
echo "========================="
|
|
15
|
-
|
|
16
|
-
cd "$(dirname "$0")/.."
|
|
17
|
-
|
|
18
|
-
# ─── Flag parsing ──────────────────────────────────────
|
|
19
|
-
POSITIONAL=()
|
|
20
|
-
REQUIRE_EVIDENCE=false
|
|
21
|
-
for arg in "$@"; do
|
|
22
|
-
case "$arg" in
|
|
23
|
-
--require-evidence)
|
|
24
|
-
REQUIRE_EVIDENCE=true
|
|
25
|
-
;;
|
|
26
|
-
--with-desktop)
|
|
27
|
-
echo "ℹ️ --with-desktop is no longer needed; GitHub Actions builds desktop assets after release publication."
|
|
28
|
-
;;
|
|
29
|
-
*)
|
|
30
|
-
POSITIONAL+=("$arg")
|
|
31
|
-
;;
|
|
32
|
-
esac
|
|
33
|
-
done
|
|
34
|
-
set -- "${POSITIONAL[@]}"
|
|
35
|
-
|
|
36
|
-
run_electron_release_checks() {
|
|
37
|
-
echo "🖥️ Checking Electron npm boundary..."
|
|
38
|
-
npm run check:electron-no-native
|
|
39
|
-
|
|
40
|
-
echo "🖥️ Type checking Electron shell..."
|
|
41
|
-
npm --prefix electron run typecheck
|
|
42
|
-
|
|
43
|
-
echo "🖥️ Building Electron shell..."
|
|
44
|
-
npm --prefix electron run build
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
ELECTRON_RELEASE_NOTES_BASE="### Desktop / Electron
|
|
48
|
-
- Electron shell validated with \`npm --prefix electron run typecheck\` and \`npm --prefix electron run build\`.
|
|
49
|
-
- npm package boundary validated with \`npm run check:electron-no-native\`; Electron app artifacts remain outside the npm package.
|
|
50
|
-
- Desktop app distribution remains separate from \`npm install -g cli-jaw\`.
|
|
51
|
-
- macOS, Windows, and Linux desktop assets are built by GitHub Actions after this release is published, then attached to this GitHub Release."
|
|
52
|
-
|
|
53
|
-
ELECTRON_RELEASE_NOTES_UNSIGNED="
|
|
54
|
-
#### ⚠️ Desktop app downloads are unsigned
|
|
55
|
-
The desktop assets attached by GitHub Actions are **unsigned** (no Apple Developer ID / Windows code-signing cert configured).
|
|
56
|
-
|
|
57
|
-
- macOS: Gatekeeper will block first launch. Either right-click → Open → Open, or remove the quarantine attribute:
|
|
58
|
-
\`\`\`sh
|
|
59
|
-
xattr -d com.apple.quarantine /Applications/cli-jaw.app
|
|
60
|
-
\`\`\`
|
|
61
|
-
- Windows: SmartScreen will warn on first run. Click \"More info\" → \"Run anyway\".
|
|
62
|
-
- Linux: AppImage downloads may need execute permission before launch.
|
|
63
|
-
- For trusted distribution, install via \`npm install -g cli-jaw\` instead."
|
|
64
|
-
|
|
65
|
-
ELECTRON_RELEASE_NOTES="$ELECTRON_RELEASE_NOTES_BASE$ELECTRON_RELEASE_NOTES_UNSIGNED"
|
|
66
|
-
|
|
67
|
-
# ─── Version detection ─────────────────────────────────
|
|
68
|
-
NPM_LATEST=$(npm view cli-jaw dist-tags.latest 2>/dev/null || echo "0.0.0")
|
|
69
|
-
PKG_VERSION=$(node -p "require('./package.json').version")
|
|
70
|
-
echo "📡 npm latest: $NPM_LATEST"
|
|
71
|
-
echo "📦 package.json: $PKG_VERSION"
|
|
72
|
-
|
|
73
|
-
PREV_TAG=$(git tag --sort=-v:refname | grep -E '^v[0-9]' | head -1)
|
|
74
|
-
|
|
75
|
-
semver_cmp() {
|
|
76
|
-
node - "$1" "$2" <<'NODE'
|
|
77
|
-
const [a, b] = process.argv.slice(2).map((value) => String(value || '0.0.0').replace(/-.*/, ''));
|
|
78
|
-
function parts(value) {
|
|
79
|
-
const match = value.match(/^(\d+)\.(\d+)\.(\d+)$/);
|
|
80
|
-
if (!match) return [0, 0, 0];
|
|
81
|
-
return match.slice(1).map(Number);
|
|
82
|
-
}
|
|
83
|
-
const pa = parts(a);
|
|
84
|
-
const pb = parts(b);
|
|
85
|
-
for (let i = 0; i < 3; i += 1) {
|
|
86
|
-
if (pa[i] > pb[i]) process.exit(1);
|
|
87
|
-
if (pa[i] < pb[i]) process.exit(2);
|
|
88
|
-
}
|
|
89
|
-
process.exit(0);
|
|
90
|
-
NODE
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
# Sync package.json to npm latest only if this checkout is behind npm.
|
|
94
|
-
# Never move a release checkout backwards just because npm latest has not
|
|
95
|
-
# caught up yet; failed OTP publishes commonly leave local tags ahead.
|
|
96
|
-
CLEAN_NPM=$(echo "$NPM_LATEST" | sed 's/-.*//')
|
|
97
|
-
CLEAN_PKG=$(echo "$PKG_VERSION" | sed 's/-.*//')
|
|
98
|
-
if [ "$CLEAN_PKG" != "$CLEAN_NPM" ] && [ "$CLEAN_NPM" != "0.0.0" ]; then
|
|
99
|
-
CMP=0
|
|
100
|
-
semver_cmp "$CLEAN_PKG" "$CLEAN_NPM" || CMP=$?
|
|
101
|
-
if [ "$CMP" -eq 2 ]; then
|
|
102
|
-
echo "⚠️ package.json ($CLEAN_PKG) is behind npm ($CLEAN_NPM). Syncing forward..."
|
|
103
|
-
npm version "$CLEAN_NPM" --no-git-tag-version --allow-same-version
|
|
104
|
-
else
|
|
105
|
-
echo "ℹ️ package.json ($CLEAN_PKG) is ahead of npm ($CLEAN_NPM); keeping checkout version."
|
|
106
|
-
fi
|
|
107
|
-
fi
|
|
108
|
-
|
|
109
|
-
# ─── Build ─────────────────────────────────────────────
|
|
110
|
-
echo "📦 Building backend (tsc)..."
|
|
111
|
-
./node_modules/.bin/tsc
|
|
112
|
-
|
|
113
|
-
echo "📦 Building frontend (vite)..."
|
|
114
|
-
npx vite build
|
|
115
|
-
|
|
116
|
-
run_electron_release_checks
|
|
117
|
-
|
|
118
|
-
# ─── Version bump ──────────────────────────────────────
|
|
119
|
-
BUMP_ARG="${1:-patch}"
|
|
120
|
-
|
|
121
|
-
# If arg looks like a semver (x.y.z), use it directly
|
|
122
|
-
if [[ "$BUMP_ARG" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
123
|
-
npm version "$BUMP_ARG" --no-git-tag-version
|
|
124
|
-
else
|
|
125
|
-
# patch (default), minor, or major
|
|
126
|
-
npm version "$BUMP_ARG" --no-git-tag-version
|
|
127
|
-
fi
|
|
128
|
-
|
|
129
|
-
VERSION=$(node -p "require('./package.json').version")
|
|
130
|
-
echo "📌 New version: $VERSION"
|
|
131
|
-
|
|
132
|
-
# Desktop artifacts are named from the version COMMITTED in
|
|
133
|
-
# electron/package.json, and desktop-release.yml builds from a tag checkout --
|
|
134
|
-
# so this has to happen here, before the release commit, not at build time.
|
|
135
|
-
echo "🖥️ Syncing Electron version to $VERSION..."
|
|
136
|
-
node scripts/sync-electron-version.cjs
|
|
137
|
-
|
|
138
|
-
# ─── Collect changelog ─────────────────────────────────
|
|
139
|
-
if [ -n "$PREV_TAG" ]; then
|
|
140
|
-
CHANGELOG=$(git log "$PREV_TAG"..HEAD --pretty=format:"- %s" --no-merges | head -50)
|
|
141
|
-
COMMIT_COUNT=$(git rev-list "$PREV_TAG"..HEAD --count)
|
|
142
|
-
else
|
|
143
|
-
CHANGELOG=$(git log --oneline -20 --pretty=format:"- %s" --no-merges)
|
|
144
|
-
COMMIT_COUNT="?"
|
|
145
|
-
fi
|
|
146
|
-
|
|
147
|
-
echo ""
|
|
148
|
-
echo "📝 Changes since ${PREV_TAG:-'(none)'} ($COMMIT_COUNT commits):"
|
|
149
|
-
echo "$CHANGELOG" | head -15
|
|
150
|
-
echo ""
|
|
151
|
-
|
|
152
|
-
# ─── Release gates ────────────────────────────────────
|
|
153
|
-
echo "🛡️ Running release gates (gate:all)..."
|
|
154
|
-
npm run gate:all
|
|
155
|
-
|
|
156
|
-
if [ "$REQUIRE_EVIDENCE" = true ]; then
|
|
157
|
-
node scripts/require-release-evidence.mjs
|
|
158
|
-
else
|
|
159
|
-
node scripts/require-release-evidence.mjs || echo "⚠️ Evidence gate skipped (pass --require-evidence to enforce)"
|
|
160
|
-
fi
|
|
161
|
-
|
|
162
|
-
# ─── Commit + Tag + Push ──────────────────────────────
|
|
163
|
-
echo "🏷️ Creating git tag v$VERSION..."
|
|
164
|
-
git add package.json package-lock.json electron/package.json electron/package-lock.json
|
|
165
|
-
git commit -m "[agent] chore: release v$VERSION" --allow-empty
|
|
166
|
-
git tag "v$VERSION"
|
|
167
|
-
# HEAD:main, not main. `git push origin main` pushes the LOCAL main branch,
|
|
168
|
-
# which is not necessarily the tree just built, committed and tagged: releases
|
|
169
|
-
# are cut from whatever branch is checked out, and a local main left behind at
|
|
170
|
-
# an older release would be published instead. Caught before a real release
|
|
171
|
-
# with local main at v2.2.7 while the release commit sat on dev.
|
|
172
|
-
git push origin HEAD:main
|
|
173
|
-
git push origin "v$VERSION"
|
|
174
|
-
|
|
175
|
-
# ─── GitHub Release with changelog ─────────────────────
|
|
176
|
-
# IMPORTANT: create/update the GitHub Release stub before the publish workflow.
|
|
177
|
-
# npm publishing is handled by .github/workflows/publish.yml through GitHub OIDC
|
|
178
|
-
# Trusted Publishing after the main push. Keeping the release record in
|
|
179
|
-
# GitHub first makes the release visible and gives desktop-release.yml a stable
|
|
180
|
-
# release target for artifact uploads.
|
|
181
|
-
echo "📋 Creating GitHub Release..."
|
|
182
|
-
RELEASE_BODY="## Release v$VERSION
|
|
183
|
-
|
|
184
|
-
**Previous**: ${PREV_TAG:-'(first release)'}
|
|
185
|
-
**Commits**: $COMMIT_COUNT
|
|
186
|
-
|
|
187
|
-
### Changes
|
|
188
|
-
$CHANGELOG
|
|
189
|
-
|
|
190
|
-
$ELECTRON_RELEASE_NOTES"
|
|
191
|
-
|
|
192
|
-
RELEASE_CREATED=false
|
|
193
|
-
if command -v gh &>/dev/null; then
|
|
194
|
-
if gh release view "v$VERSION" &>/dev/null; then
|
|
195
|
-
echo "ℹ️ GitHub Release v$VERSION already exists — updating notes."
|
|
196
|
-
gh release edit "v$VERSION" --title "v$VERSION" --notes "$RELEASE_BODY" --latest \
|
|
197
|
-
&& RELEASE_CREATED=true \
|
|
198
|
-
|| echo "⚠️ Failed to update existing GitHub Release v$VERSION (continuing)."
|
|
199
|
-
elif gh release create "v$VERSION" --title "v$VERSION" --notes "$RELEASE_BODY" --latest; then
|
|
200
|
-
RELEASE_CREATED=true
|
|
201
|
-
echo "✅ GitHub Release v$VERSION created!"
|
|
202
|
-
echo "🖥️ Desktop assets will be built by the Desktop Release GitHub Actions workflow."
|
|
203
|
-
else
|
|
204
|
-
# Do NOT abort: the tag is already pushed and we still want to publish.
|
|
205
|
-
# Surface loudly so the release can be backfilled manually.
|
|
206
|
-
echo "❌ GitHub Release v$VERSION FAILED to create. Tag is pushed; backfill with:"
|
|
207
|
-
echo " gh release create v$VERSION --title v$VERSION --notes-file <(...) --latest"
|
|
208
|
-
fi
|
|
209
|
-
else
|
|
210
|
-
echo "⚠️ Skipped GitHub Release (gh CLI not found)"
|
|
211
|
-
fi
|
|
212
|
-
|
|
213
|
-
# ─── npm publish ───────────────────────────────────────
|
|
214
|
-
echo "🚀 npm publish is handled by .github/workflows/publish.yml after the main push."
|
|
215
|
-
echo " Workflow: https://github.com/lidge-jun/cli-jaw/actions/workflows/publish.yml?query=branch%3Amain"
|
|
216
|
-
|
|
217
|
-
echo ""
|
|
218
|
-
echo "✅ cli-jaw@$VERSION release queued!"
|
|
219
|
-
echo " Install: npm install -g cli-jaw"
|
|
220
|
-
if [ "$RELEASE_CREATED" = true ]; then
|
|
221
|
-
echo " Release: https://github.com/lidge-jun/cli-jaw/releases/tag/v$VERSION"
|
|
222
|
-
else
|
|
223
|
-
echo " ⚠️ GitHub Release for v$VERSION was NOT created — backfill it manually."
|
|
224
|
-
fi
|
|
File without changes
|