klaudius 0.21.3 → 0.22.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,8 +5,9 @@ node ${process.version}, platform ${process.platform}, tty=${process.stdin.isTTY
5
5
  `)}catch{}}var C=p,g=!1;function N(){if(g)return;g=!0;let t=process.exit.bind(process);process.exit=(e=>{let o=new Error("process.exit called").stack??"(no stack)";return T("process.exit called",{code:e,stack:o.split(`
6
6
  `).slice(0,8).join(" | ")}),t(e)})}function R(t){if(!t)return;let e=t.toLowerCase();for(let[o,s]of Object.entries(u))if(s.smtpHost===e||s.altHostSets?.some(a=>a.smtpHost===e))return o}function _(t,e,o){if(t==="custom")return[];if(!Object.hasOwn(u,t))return[];let s=u[t];return[{label:`standard ${s.label}`,smtpHost:s.smtpHost,imapHost:s.imapHost},...s.altHostSets??[]].filter(n=>!(n.smtpHost===e&&n.imapHost===o))}var u={gmail:{label:"Gmail",smtpHost:"smtp.gmail.com",smtpPort:587,imapHost:"imap.gmail.com",imapPort:993,sentFolder:"[Gmail]/Sent Mail",inboxFolder:"INBOX",appPasswordHelp:`Generate at https://myaccount.google.com/apppasswords
7
7
  (2-step verification must be enabled on your Google account first;
8
- Google won't accept your normal account password from third-party apps).`},outlook:{label:"Outlook / Office 365",smtpHost:"smtp-mail.outlook.com",smtpPort:587,imapHost:"outlook.office365.com",imapPort:993,sentFolder:"Sent Items",inboxFolder:"INBOX",appPasswordHelp:`Generate at https://account.microsoft.com/security \u2192 Advanced security options \u2192 App passwords
9
- (2-step verification must be enabled first).`},ionos:{label:"IONOS",smtpHost:"smtp.ionos.co.uk",smtpPort:587,imapHost:"imap.ionos.co.uk",imapPort:993,sentFolder:"Sent Items",inboxFolder:"INBOX",usesAccountPassword:!0,appPasswordHelp:`Use your normal IONOS mailbox password.
8
+ Google won't accept your normal account password from third-party apps).`},outlook:{label:"Outlook / Microsoft 365",smtpHost:"smtp-mail.outlook.com",smtpPort:587,imapHost:"outlook.office365.com",imapPort:993,sentFolder:"Sent Items",inboxFolder:"INBOX",oauthOnly:!0,appPasswordHelp:`Outlook / Microsoft 365 mailboxes don't use passwords with Klaudius \u2014
9
+ Microsoft retired app-password access. Connect with the one-time
10
+ browser sign-in instead: python3 scripts/ms_oauth.py login`},ionos:{label:"IONOS",smtpHost:"smtp.ionos.co.uk",smtpPort:587,imapHost:"imap.ionos.co.uk",imapPort:993,sentFolder:"Sent Items",inboxFolder:"INBOX",usesAccountPassword:!0,appPasswordHelp:`Use your normal IONOS mailbox password.
10
11
  (IONOS doesn't require app passwords for IMAP/SMTP unless you've enabled 2FA \u2014
11
12
  with 2FA on, generate an app password in IONOS account security settings and
12
13
  use that here instead.)`,serverHelp:`IONOS runs country-specific mail servers. The standard servers here
@@ -57,4 +58,4 @@ login, so the server isn't the problem. In Titan webmail, check:
57
58
  many providers require a generated app-specific password, while some
58
59
  (e.g. IONOS, Titan) just use your normal mailbox password. If your
59
60
  provider offers app passwords, prefer one \u2014 it's revocable on its own
60
- and doesn't expose your account login.`;function l(t){if(!(t instanceof Error))return String(t);let e=t.cause;if(!e||!(e instanceof Error))return t.message;let o=e.code,s=e.message;return o&&s&&s!==o?`${t.message} (${o}: ${s})`:o?`${t.message} (${o})`:s?`${t.message} (${s})`:t.message}async function j(t){let e;try{let n=await fetch(`https://api.telegram.org/bot${encodeURIComponent(t.botToken)}/getUpdates`);if(e=await n.json(),!e.ok){let r=e.description??`HTTP ${n.status}`;return/webhook/i.test(r)?{ok:!1,reason:"webhook_set",message:"This bot has a webhook set, so getUpdates is unavailable. Auto-detect can't run; you'll need to paste the chat ID manually."}:{ok:!1,reason:"bad_token",message:`Telegram rejected the bot token: ${r}. Double-check the token from @BotFather.`}}}catch(n){return{ok:!1,reason:"network",message:`Could not reach Telegram: ${l(n)}`}}let o=e.result??[];if(o.length===0)return{ok:!1,reason:"no_messages",message:"No messages yet. Send your bot a regular text message in Telegram, then retry."};let s=o[o.length-1],a=s?.message?.chat?.id??s?.edited_message?.chat?.id??s?.channel_post?.chat?.id;return a===void 0?{ok:!1,reason:"no_messages",message:"Got an update from Telegram but couldn't find a chat ID in it. Send another text message and retry."}:{ok:!0,chatId:String(a)}}async function H(t){try{let e=await fetch(`https://api.telegram.org/bot${encodeURIComponent(t.botToken)}/getMe`),o=await e.json();return o.ok?{ok:!0,botHandle:o.result?.username?`@${o.result.username}`:o.result?.first_name}:{ok:!1,failedAt:"token",message:`Telegram rejected the bot token: ${o.description??`HTTP ${e.status}`}. Generate a new token by messaging @BotFather \u2192 /mytoken or /newbot.`}}catch(e){return{ok:!1,failedAt:"token",message:`Could not reach Telegram: ${l(e)}`}}}async function B(t){let e=await H({botToken:t.botToken});if(!e.ok)return e;let o=e.botHandle;try{let s=await fetch(`https://api.telegram.org/bot${encodeURIComponent(t.botToken)}/sendMessage`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({chat_id:t.chatId,text:"\u2713 Klaudius connected. You'll get a notification here whenever outreach lands or a candidate replies. (You can ignore this setup message.)",disable_notification:!0})}),a=await s.json();if(!a.ok){let n=a.description??`HTTP ${s.status}`,r="";return/chat not found/i.test(n)?r=" The chat_id is wrong, or the bot hasn't been added to that chat yet. If it's a group, you must add the bot to the group first; then send /start to it.":(/bot was blocked/i.test(n)||/forbidden/i.test(n))&&(r=" The bot has been blocked by the chat, or never received a /start. Open Telegram, find your bot, and send /start to wake it up."),{ok:!1,failedAt:"chat",botHandle:o,message:`Telegram delivered the bot token check but couldn't send to chat_id ${t.chatId}: ${n}.${r}`}}}catch(s){return{ok:!1,failedAt:"chat",botHandle:o,message:`Could not reach Telegram for chat_id check: ${l(s)}`}}return{ok:!0,botHandle:o}}function Z(t){let e=t.trim();if(e.length===0)return"Required \u2014 without this Klaudius can't deploy generated sites";if(e.startsWith("http://")||e.startsWith("https://"))return"This is the SSH/rsync destination, not a URL \u2014 e.g. deploy@yourvps:/var/www/klaudius-sites";if(/^[A-Za-z]:[\\/]/.test(e))return"That's a local Windows path \u2014 the target must be a remote rsync destination like user@host:/path";if(!/^[^\s:]+:\/?.+/.test(e))return"Should look like user@host:/path (an rsync-over-SSH destination)"}function K(t){let e=t.trim();if(e.length===0)return"Required \u2014 outreach links every pitch to the live site URL";if(!/^https?:\/\//.test(e))return"Must start with https:// (or http://)";if(!e.includes("{slug}"))return"Must contain {slug} \u2014 it's replaced with each client's slug";if(!(e.replace(/^https?:\/\//,"").split("/")[0]??"").includes("{slug}"))return"{slug} must be in the hostname (e.g. https://{slug}.sites.example.com) \u2014 a path like /sites/{slug} breaks static-export asset links"}var S={\u0410:"A",\u0430:"a",\u0412:"B",\u0432:"b",\u0415:"E",\u0435:"e",\u041A:"K",\u043A:"k",\u041C:"M",\u043C:"m",\u041D:"H",\u043D:"h",\u041E:"O",\u043E:"o",\u0420:"P",\u0440:"p",\u0421:"C",\u0441:"c",\u0422:"T",\u0442:"t",\u0423:"Y",\u0443:"y",\u0425:"X",\u0445:"x",\u0406:"I",\u0456:"i",\u0405:"S",\u0455:"s",\u0408:"J",\u0458:"j",\u0417:"3",\u0437:"3",\u0427:"4",\u0431:"6",\u0391:"A",\u03B1:"a",\u0392:"B",\u03B2:"b",\u0395:"E",\u03B5:"e",\u0396:"Z",\u0397:"H",\u0399:"I",\u03B9:"i",\u039A:"K",\u03BA:"k",\u039C:"M",\u039D:"N",\u039F:"O",\u03BF:"o",\u03A1:"P",\u03C1:"p",\u03A4:"T",\u03C4:"t",\u03A5:"Y",\u03A7:"X",\u03C7:"x",\u03BD:"v","\u2010":"-","\u2011":"-","\u2012":"-","\u2013":"-","\u2014":"-","\u2015":"-","\u2212":"-","\uFF3F":"_"},I=/[\u200B-\u200F\u202A-\u202E\u2060\uFEFF\u00AD]/g,v=/[\u0000-\u001F\u007F]/g;function P(t){let e=t.replace(I,"").replace(v,"").trim();return{value:e,changed:e!==t,repairs:[],unrepairable:[]}}function f(t){let o=P(t).value.normalize("NFKC").replace(/\s+/g,"");o.length>=2&&(o[0]==='"'&&o.at(-1)==='"'||o[0]==="'"&&o.at(-1)==="'")&&(o=o.slice(1,-1).trim());let s=[],a=[],n="";for(let r of o){let i=r.codePointAt(0);if(i<128){n+=r;continue}let c=S[r],d=n.length+1;c?(s.push(`position ${d}: '${r}' (${O(i)}, U+${b(i)}) \u2192 '${c}'`),n+=c):(a.push({position:d,char:r,code:i}),n+=r)}return{value:n,changed:n!==t,repairs:s,unrepairable:a}}var A=/^AIza[0-9A-Za-z_-]{35}$/;function x(t){return A.test(t)}function X(t){let e=f(t);if(e.value.length===0)return"Required \u2014 without this Klaudius can't find candidates.";if(e.unrepairable.length>0){let o=e.unrepairable[0];return`Character ${o.position} of this key is '${o.char}' (U+${b(o.code)}), which isn't a character a Google key can contain. Copy the key with the copy button next to it in the Cloud Console \u2014 reading it off a screenshot or retyping it introduces look-alike characters.`}}function q(t){let e=f(t).value;if(!x(e))return e.startsWith("AIza")?e.length!==39?`A Google API key is normally 39 characters \u2014 this one is ${e.length}. `+(e.length>39?"It looks like two keys ran together, so paste just the one key.":"It looks truncated, so copy it again with the copy button next to it in the Cloud Console."):"This key contains characters a Google key normally doesn't. Re-copy it with the copy button in the Cloud Console.":`A Google API key normally starts with "AIza" \u2014 this one starts with "${e.slice(0,4)}". Check you copied the API key itself (Cloud Console \u2192 Credentials), not a project ID or client ID.`}function b(t){return t.toString(16).toUpperCase().padStart(4,"0")}function O(t){return t>=1024&&t<=1279?"Cyrillic":t>=880&&t<=1023?"Greek":"non-Latin"}export{T as a,C as b,N as c,R as d,_ as e,u as f,U as g,l as h,j as i,H as j,B as k,Z as l,K as m,P as n,f as o,X as p,q};
61
+ and doesn't expose your account login.`;function l(t){if(!(t instanceof Error))return String(t);let e=t.cause;if(!e||!(e instanceof Error))return t.message;let o=e.code,s=e.message;return o&&s&&s!==o?`${t.message} (${o}: ${s})`:o?`${t.message} (${o})`:s?`${t.message} (${s})`:t.message}async function j(t){let e;try{let n=await fetch(`https://api.telegram.org/bot${encodeURIComponent(t.botToken)}/getUpdates`);if(e=await n.json(),!e.ok){let r=e.description??`HTTP ${n.status}`;return/webhook/i.test(r)?{ok:!1,reason:"webhook_set",message:"This bot has a webhook set, so getUpdates is unavailable. Auto-detect can't run; you'll need to paste the chat ID manually."}:{ok:!1,reason:"bad_token",message:`Telegram rejected the bot token: ${r}. Double-check the token from @BotFather.`}}}catch(n){return{ok:!1,reason:"network",message:`Could not reach Telegram: ${l(n)}`}}let o=e.result??[];if(o.length===0)return{ok:!1,reason:"no_messages",message:"No messages yet. Send your bot a regular text message in Telegram, then retry."};let s=o[o.length-1],a=s?.message?.chat?.id??s?.edited_message?.chat?.id??s?.channel_post?.chat?.id;return a===void 0?{ok:!1,reason:"no_messages",message:"Got an update from Telegram but couldn't find a chat ID in it. Send another text message and retry."}:{ok:!0,chatId:String(a)}}async function H(t){try{let e=await fetch(`https://api.telegram.org/bot${encodeURIComponent(t.botToken)}/getMe`),o=await e.json();return o.ok?{ok:!0,botHandle:o.result?.username?`@${o.result.username}`:o.result?.first_name}:{ok:!1,failedAt:"token",message:`Telegram rejected the bot token: ${o.description??`HTTP ${e.status}`}. Generate a new token by messaging @BotFather \u2192 /mytoken or /newbot.`}}catch(e){return{ok:!1,failedAt:"token",message:`Could not reach Telegram: ${l(e)}`}}}async function B(t){let e=await H({botToken:t.botToken});if(!e.ok)return e;let o=e.botHandle;try{let s=await fetch(`https://api.telegram.org/bot${encodeURIComponent(t.botToken)}/sendMessage`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({chat_id:t.chatId,text:"\u2713 Klaudius connected. You'll get a notification here whenever outreach lands or a candidate replies. (You can ignore this setup message.)",disable_notification:!0})}),a=await s.json();if(!a.ok){let n=a.description??`HTTP ${s.status}`,r="";return/chat not found/i.test(n)?r=" The chat_id is wrong, or the bot hasn't been added to that chat yet. If it's a group, you must add the bot to the group first; then send /start to it.":(/bot was blocked/i.test(n)||/forbidden/i.test(n))&&(r=" The bot has been blocked by the chat, or never received a /start. Open Telegram, find your bot, and send /start to wake it up."),{ok:!1,failedAt:"chat",botHandle:o,message:`Telegram delivered the bot token check but couldn't send to chat_id ${t.chatId}: ${n}.${r}`}}}catch(s){return{ok:!1,failedAt:"chat",botHandle:o,message:`Could not reach Telegram for chat_id check: ${l(s)}`}}return{ok:!0,botHandle:o}}function Z(t){let e=t.trim();if(e.length===0)return"Required \u2014 without this Klaudius can't deploy generated sites";if(e.startsWith("http://")||e.startsWith("https://"))return"This is the SSH/rsync destination, not a URL \u2014 e.g. deploy@yourvps:/var/www/klaudius-sites";if(/^[A-Za-z]:[\\/]/.test(e))return"That's a local Windows path \u2014 the target must be a remote rsync destination like user@host:/path";if(!/^[^\s:]+:\/?.+/.test(e))return"Should look like user@host:/path (an rsync-over-SSH destination)"}function K(t){let e=t.trim();if(e.length===0)return"Required \u2014 outreach links every pitch to the live site URL";if(!/^https?:\/\//.test(e))return"Must start with https:// (or http://)";if(!e.includes("{slug}"))return"Must contain {slug} \u2014 it's replaced with each client's slug";if(!(e.replace(/^https?:\/\//,"").split("/")[0]??"").includes("{slug}"))return"{slug} must be in the hostname (e.g. https://{slug}.sites.example.com) \u2014 a path like /sites/{slug} breaks static-export asset links"}var S={\u0410:"A",\u0430:"a",\u0412:"B",\u0432:"b",\u0415:"E",\u0435:"e",\u041A:"K",\u043A:"k",\u041C:"M",\u043C:"m",\u041D:"H",\u043D:"h",\u041E:"O",\u043E:"o",\u0420:"P",\u0440:"p",\u0421:"C",\u0441:"c",\u0422:"T",\u0442:"t",\u0423:"Y",\u0443:"y",\u0425:"X",\u0445:"x",\u0406:"I",\u0456:"i",\u0405:"S",\u0455:"s",\u0408:"J",\u0458:"j",\u0417:"3",\u0437:"3",\u0427:"4",\u0431:"6",\u0391:"A",\u03B1:"a",\u0392:"B",\u03B2:"b",\u0395:"E",\u03B5:"e",\u0396:"Z",\u0397:"H",\u0399:"I",\u03B9:"i",\u039A:"K",\u03BA:"k",\u039C:"M",\u039D:"N",\u039F:"O",\u03BF:"o",\u03A1:"P",\u03C1:"p",\u03A4:"T",\u03C4:"t",\u03A5:"Y",\u03A7:"X",\u03C7:"x",\u03BD:"v","\u2010":"-","\u2011":"-","\u2012":"-","\u2013":"-","\u2014":"-","\u2015":"-","\u2212":"-","\uFF3F":"_"},I=/[\u200B-\u200F\u202A-\u202E\u2060\uFEFF\u00AD]/g,P=/[\u0000-\u001F\u007F]/g;function v(t){let e=t.replace(I,"").replace(P,"").trim();return{value:e,changed:e!==t,repairs:[],unrepairable:[]}}function f(t){let o=v(t).value.normalize("NFKC").replace(/\s+/g,"");o.length>=2&&(o[0]==='"'&&o.at(-1)==='"'||o[0]==="'"&&o.at(-1)==="'")&&(o=o.slice(1,-1).trim());let s=[],a=[],n="";for(let r of o){let i=r.codePointAt(0);if(i<128){n+=r;continue}let c=S[r],d=n.length+1;c?(s.push(`position ${d}: '${r}' (${O(i)}, U+${b(i)}) \u2192 '${c}'`),n+=c):(a.push({position:d,char:r,code:i}),n+=r)}return{value:n,changed:n!==t,repairs:s,unrepairable:a}}var x=/^AIza[0-9A-Za-z_-]{35}$/;function A(t){return x.test(t)}function X(t){let e=f(t);if(e.value.length===0)return"Required \u2014 without this Klaudius can't find candidates.";if(e.unrepairable.length>0){let o=e.unrepairable[0];return`Character ${o.position} of this key is '${o.char}' (U+${b(o.code)}), which isn't a character a Google key can contain. Copy the key with the copy button next to it in the Cloud Console \u2014 reading it off a screenshot or retyping it introduces look-alike characters.`}}function q(t){let e=f(t).value;if(!A(e))return e.startsWith("AIza")?e.length!==39?`A Google API key is normally 39 characters \u2014 this one is ${e.length}. `+(e.length>39?"It looks like two keys ran together, so paste just the one key.":"It looks truncated, so copy it again with the copy button next to it in the Cloud Console."):"This key contains characters a Google key normally doesn't. Re-copy it with the copy button in the Cloud Console.":`A Google API key normally starts with "AIza" \u2014 this one starts with "${e.slice(0,4)}". Check you copied the API key itself (Cloud Console \u2192 Credentials), not a project ID or client ID.`}function b(t){return t.toString(16).toUpperCase().padStart(4,"0")}function O(t){return t>=1024&&t<=1279?"Cyrillic":t>=880&&t<=1023?"Greek":"non-Latin"}export{T as a,C as b,N as c,R as d,_ as e,u as f,U as g,l as h,j as i,H as j,B as k,Z as l,K as m,v as n,f as o,X as p,q};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "klaudius",
3
- "version": "0.21.3",
3
+ "version": "0.22.2",
4
4
  "description": "Scaffold and maintain a Klaudius project — an autonomous web agency pipeline that runs on Claude Code (find, build, deploy, outreach end-to-end).",
5
5
  "homepage": "https://klaudius.dev",
6
6
  "bugs": {