@quantiya/codevibe-claude-plugin 1.0.16 → 1.0.17
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.
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codevibe-claude",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.17",
|
|
4
4
|
"description": "Sync Claude Code sessions with iOS mobile app via AWS backend. Control Claude Code from your phone with real-time bidirectional synchronization.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "CodeVibe Team"
|
|
@@ -45,30 +45,51 @@ if [ -n "$TRANSCRIPT_PATH" ] && [ -f "$TRANSCRIPT_PATH" ]; then
|
|
|
45
45
|
SENT_UUIDS_CONTENT=$(cat "$SENT_UUIDS_FILE")
|
|
46
46
|
fi
|
|
47
47
|
|
|
48
|
-
# Single jq invocation: find
|
|
49
|
-
#
|
|
48
|
+
# Single jq invocation: find the most recent REAL user prompt, then take all
|
|
49
|
+
# assistant messages after it in file order, filter out already-sent UUIDs,
|
|
50
|
+
# extract text content.
|
|
51
|
+
#
|
|
52
|
+
# Previous implementation used a UUID-parent chain starting from the "last
|
|
53
|
+
# user message" — but because the transcript includes tool_result entries as
|
|
54
|
+
# type "user", the chain starting point was the last tool_result, not the
|
|
55
|
+
# real user prompt. That meant only the ONE assistant message immediately
|
|
56
|
+
# after the last tool_result was captured, and every earlier assistant text
|
|
57
|
+
# block in the same turn was silently lost. This was bug #39 (2026-04-14):
|
|
58
|
+
# users saw ~1/10 of mid-turn assistant text on mobile during long responses.
|
|
59
|
+
#
|
|
60
|
+
# Fix: a real user prompt has at least one content block of type "text".
|
|
61
|
+
# Tool_result user messages have content blocks of type "tool_result". We
|
|
62
|
+
# find the INDEX of the last real user prompt in file order, then take every
|
|
63
|
+
# subsequent assistant message (they're all part of the current turn).
|
|
50
64
|
ASSISTANT_MESSAGES=$(jq -r --slurp --arg sent "$SENT_UUIDS_CONTENT" '
|
|
51
|
-
#
|
|
52
|
-
|
|
53
|
-
|
|
65
|
+
# Index of the last "real" user prompt. A real prompt has .message.content
|
|
66
|
+
# that is EITHER a string (simple text prompt) OR an array containing at
|
|
67
|
+
# least one content block of type "text" (mixed content). Tool_result user
|
|
68
|
+
# messages have an array of content blocks where every block is type
|
|
69
|
+
# "tool_result" — those are NOT real prompts and must be excluded.
|
|
70
|
+
(
|
|
71
|
+
[ range(0; length) as $i
|
|
72
|
+
| select(.[$i].type == "user" and (
|
|
73
|
+
(.[$i].message.content | type) == "string" or
|
|
74
|
+
((.[$i].message.content | type) == "array" and
|
|
75
|
+
(.[$i].message.content | any(.type? == "text")))
|
|
76
|
+
))
|
|
77
|
+
| $i
|
|
78
|
+
]
|
|
79
|
+
| last // -1
|
|
80
|
+
) as $startIdx |
|
|
81
|
+
if $startIdx < 0 then empty
|
|
54
82
|
else
|
|
55
|
-
#
|
|
56
|
-
reduce .[] as $msg (
|
|
57
|
-
[$lastUserUuid];
|
|
58
|
-
if (. | any(. == ($msg.parentUuid // ""))) then
|
|
59
|
-
. + [($msg.uuid // "")]
|
|
60
|
-
else . end
|
|
61
|
-
) as $chain |
|
|
62
|
-
# Parse sent UUIDs
|
|
83
|
+
# Parse sent UUIDs list
|
|
63
84
|
($sent | split("\n") | map(select(. != ""))) as $sentList |
|
|
64
|
-
#
|
|
85
|
+
# Take messages after the real user prompt, filter to unsent assistant text
|
|
86
|
+
.[($startIdx + 1):] |
|
|
65
87
|
.[] |
|
|
66
88
|
select(.type == "assistant") |
|
|
67
|
-
select(.uuid as $u | $chain | any(. == $u)) |
|
|
68
89
|
select(.uuid as $u | $sentList | any(. == $u) | not) |
|
|
69
90
|
{
|
|
70
91
|
uuid: .uuid,
|
|
71
|
-
text: ([.message.content[]? | select(.type == "text") | .text // empty] | join("
|
|
92
|
+
text: ([.message.content[]? | select(.type == "text") | .text // empty] | join("\n\n"))
|
|
72
93
|
} |
|
|
73
94
|
select(.text | length > 0) |
|
|
74
95
|
"\(.uuid)\t\(.text | @base64)"
|
package/hooks/stop.sh
CHANGED
|
@@ -44,29 +44,46 @@ if [ -f "$SENT_UUIDS_FILE" ]; then
|
|
|
44
44
|
SENT_UUIDS_CONTENT=$(cat "$SENT_UUIDS_FILE")
|
|
45
45
|
fi
|
|
46
46
|
|
|
47
|
-
#
|
|
48
|
-
#
|
|
47
|
+
# Find the index of the most recent REAL user prompt, then emit assistant
|
|
48
|
+
# text + tool_use events from all subsequent messages in file order.
|
|
49
|
+
#
|
|
50
|
+
# Previous implementation used a UUID-parent chain starting from the "last
|
|
51
|
+
# user message" — but tool_result messages ALSO have type "user", so the
|
|
52
|
+
# chain start was usually the last tool_result, missing every assistant
|
|
53
|
+
# message earlier in the same turn. Bug #39 (fixed 2026-04-14).
|
|
54
|
+
#
|
|
55
|
+
# A real user prompt has at least one content block of type "text". Tool_result
|
|
56
|
+
# user messages have content blocks of type "tool_result". We find the index
|
|
57
|
+
# of the last real user prompt and walk forward from there in file order.
|
|
58
|
+
#
|
|
49
59
|
# Output format: tab-separated lines: uuid\ttype\tcontent
|
|
50
60
|
# type is "text" for base64-encoded assistant text, "tool_use" for JSON tool uses
|
|
51
61
|
# Text is base64-encoded because it may contain newlines which break line-by-line read
|
|
52
62
|
TRANSCRIPT_EVENTS=$(jq -r --slurp --arg sent "$SENT_UUIDS_CONTENT" '
|
|
53
|
-
#
|
|
54
|
-
|
|
55
|
-
|
|
63
|
+
# Index of the last "real" user prompt. A real prompt has .message.content
|
|
64
|
+
# that is EITHER a string (simple text prompt) OR an array containing at
|
|
65
|
+
# least one content block of type "text" (mixed content). Tool_result user
|
|
66
|
+
# messages have an array of content blocks where every block is type
|
|
67
|
+
# "tool_result" — those are NOT real prompts and must be excluded.
|
|
68
|
+
(
|
|
69
|
+
[ range(0; length) as $i
|
|
70
|
+
| select(.[$i].type == "user" and (
|
|
71
|
+
(.[$i].message.content | type) == "string" or
|
|
72
|
+
((.[$i].message.content | type) == "array" and
|
|
73
|
+
(.[$i].message.content | any(.type? == "text")))
|
|
74
|
+
))
|
|
75
|
+
| $i
|
|
76
|
+
]
|
|
77
|
+
| last // -1
|
|
78
|
+
) as $startIdx |
|
|
79
|
+
if $startIdx < 0 then empty
|
|
56
80
|
else
|
|
57
|
-
# Build UUID chain from last user prompt
|
|
58
|
-
reduce .[] as $msg (
|
|
59
|
-
[$lastUserUuid];
|
|
60
|
-
if (. | any(. == ($msg.parentUuid // ""))) then
|
|
61
|
-
. + [($msg.uuid // "")]
|
|
62
|
-
else . end
|
|
63
|
-
) as $chain |
|
|
64
81
|
# Parse sent UUIDs
|
|
65
82
|
($sent | split("\n") | map(select(. != ""))) as $sentList |
|
|
66
|
-
#
|
|
83
|
+
# Take messages after the real user prompt
|
|
84
|
+
.[($startIdx + 1):] |
|
|
67
85
|
.[] |
|
|
68
86
|
select(.type == "assistant") |
|
|
69
|
-
select(.uuid as $u | $chain | any(. == $u)) |
|
|
70
87
|
. as $msg |
|
|
71
88
|
# Emit text content (base64-encoded to preserve newlines)
|
|
72
89
|
(
|
|
@@ -75,7 +92,7 @@ TRANSCRIPT_EVENTS=$(jq -r --slurp --arg sent "$SENT_UUIDS_CONTENT" '
|
|
|
75
92
|
if $alreadySent then
|
|
76
93
|
"\($uuid)\talready_sent\t"
|
|
77
94
|
else
|
|
78
|
-
([$msg.message.content[]? | select(.type == "text") | .text // empty] | join("
|
|
95
|
+
([$msg.message.content[]? | select(.type == "text") | .text // empty] | join("\n\n")) as $text |
|
|
79
96
|
if ($text | length) > 0 then
|
|
80
97
|
"\($uuid)\ttext\t\($text | @base64)"
|
|
81
98
|
else empty end
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
"use strict";var Ze=Object.create;var Y=Object.defineProperty;var et=Object.getOwnPropertyDescriptor;var tt=Object.getOwnPropertyNames;var nt=Object.getPrototypeOf,rt=Object.prototype.hasOwnProperty;var E=(r,e)=>()=>(r&&(e=r(r=0)),e);var Te=(r,e)=>{for(var t in e)Y(r,t,{get:e[t],enumerable:!0})},xe=(r,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let
|
|
2
|
-
${n.stack}`)):typeof n=="object"?o+=` ${JSON.stringify(n,
|
|
3
|
-
`)}catch{}if(this.enableConsole)switch(e){case"error":console.error(
|
|
4
|
-
`)}catch{}}function at(){
|
|
5
|
-
`))}function dt(r){return r.replace(/[^a-zA-Z0-9._-]/g,"_")}function Ke(r){return ae.join(ie,`${dt(r)}.json`)}function ce(r){try{let e=A.readFileSync(Ke(r),"utf-8"),t=JSON.parse(e);return t&&typeof t=="object"?t:{}}catch{return{}}}function Oe(r,e){let t=Ke(r);A.writeFileSync(t,JSON.stringify(e,null,2),{mode:384});try{A.chmodSync(t,384)}catch{}}function de(){if(ct(),P===null)throw oe??new X("Keychain backend not initialized")}async function le(r,e){return de(),P==="keytar"&&I?I.getPassword(r,e):ce(r)[e]??null}async function pe(r,e,t){if(de(),P==="keytar"&&I){await I.setPassword(r,e,t);return}let n=ce(r);n[e]=t,Oe(r,n)}async function ue(r,e){if(de(),P==="keytar"&&I)return I.deletePassword(r,e);let t=ce(r);return e in t?(delete t[e],Oe(r,t),!0):!1}var Pe,ae,A,X,P,I,ie,oe,Ne=E(()=>{"use strict";Pe=y(require("os")),ae=y(require("path")),A=y(require("fs"));L();X=class extends Error{constructor(e){super(e),this.name="KeychainBackendUnavailableError"}},P=null,I=null,ie="",oe=null});var v,K,ge,pt,U,b,Re=E(()=>{"use strict";v=y(require("crypto")),K=class extends Error{constructor(e){super(e),this.name="CryptoError"}},ge=1,pt="CodeVibe E2E v1",U=class r{constructor(){}static getInstance(){return r.instance||(r.instance=new r),r.instance}generateKeyPair(){let e=v.createECDH("prime256v1");e.generateKeys();let n=e.getPublicKey().subarray(1).toString("base64");return{privateKey:e.getPrivateKey().toString("base64"),publicKey:n}}generateSessionKey(){return v.randomBytes(32).toString("base64")}deriveSharedKey(e,t){try{let n=v.createECDH("prime256v1"),s=Buffer.from(e,"base64");n.setPrivateKey(s);let i=Buffer.concat([Buffer.from([4]),Buffer.from(t,"base64")]),o=n.computeSecret(i),a=v.hkdfSync("sha256",o,Buffer.alloc(0),Buffer.from(pt,"utf8"),32);return Buffer.from(a)}catch(n){throw new K(`Failed to derive shared key: ${n}`)}}encryptSessionKey(e,t){let n=this.generateKeyPair(),s=this.deriveSharedKey(n.privateKey,t),i=Buffer.from(e,"base64");return{encryptedKey:this.encrypt(i,s).toString("base64"),ephemeralPublicKey:n.publicKey}}decryptSessionKey(e,t){let n=this.deriveSharedKey(t,e.ephemeralPublicKey),s=Buffer.from(e.encryptedKey,"base64");return this.decrypt(s,n).toString("base64")}encryptContent(e,t){let n=Buffer.from(t,"base64"),s=Buffer.from(e,"utf8");return this.encrypt(s,n).toString("base64")}decryptContent(e,t){let n=Buffer.from(t,"base64"),s=Buffer.from(e,"base64");return this.decrypt(s,n).toString("utf8")}encryptMetadata(e,t){let n=JSON.stringify(e);return this.encryptContent(n,t)}decryptMetadata(e,t){let n=this.decryptContent(e,t);return JSON.parse(n)}encryptData(e,t){let n=Buffer.from(t,"base64");return this.encrypt(e,n)}decryptData(e,t){let n=Buffer.from(t,"base64");return this.decrypt(e,n)}encrypt(e,t){let n=v.randomBytes(12),s=v.createCipheriv("aes-256-gcm",t,n),i=Buffer.concat([s.update(e),s.final()]),o=s.getAuthTag();return Buffer.concat([n,i,o])}decrypt(e,t){let n=e.subarray(0,12),s=e.subarray(e.length-16),i=e.subarray(12,e.length-16),o=v.createDecipheriv("aes-256-gcm",t,n);o.setAuthTag(s);try{return Buffer.concat([o.update(i),o.final()])}catch{throw new K("Decryption failed: Invalid ciphertext or authentication tag")}}serializePrivateKey(e){return e}deserializePrivateKey(e){return e}},b=U.getInstance()});var Q=E(()=>{"use strict";Re()});function h(){let r=process.env.ENVIRONMENT;return r==="development"||r==="production"?r:"production"}function ee(r){let e=r||h();return Z={...O[e],aws:{...O[e].aws,region:process.env.AWS_REGION||O[e].aws.region,appsyncUrl:process.env.APPSYNC_URL||O[e].aws.appsyncUrl,cognitoUserPoolId:process.env.COGNITO_USER_POOL_ID||O[e].aws.cognitoUserPoolId,cognitoClientId:process.env.COGNITO_CLIENT_ID||O[e].aws.cognitoClientId,cognitoDomain:process.env.COGNITO_DOMAIN||O[e].aws.cognitoDomain}},$e=!0,Z}function f(){return(!$e||!Z)&&ee(),Z}var V,W,O,Z,$e,Le=E(()=>{"use strict";V=y(require("os")),W=y(require("path")),O={development:{environment:"development",aws:{region:"us-east-1",appsyncUrl:"https://te6rjr37sbfpjc4fiunmb2tgy4.appsync-api.us-east-1.amazonaws.com/graphql",cognitoUserPoolId:"us-east-1_yVwWDPvvJ",cognitoClientId:"e9r5apv6v5uui3l928r2ris0r",cognitoDomain:"codevibe-development.auth.us-east-1.amazoncognito.com"},keychain:{serviceName:"ai.quantiya.app.codevibe"},server:{port:3456,host:"127.0.0.1",dynamicPort:!0},claude:{command:"claude",defaultTimeout:6e4},codex:{command:"codex",defaultTimeout:6e4,sessionsDir:W.default.join(V.default.homedir(),".codex","sessions"),approvalTimeoutMs:5e3},gemini:{command:"gemini",defaultTimeout:6e4,transcriptDir:W.default.join(V.default.homedir(),".gemini","tmp")}},production:{environment:"production",aws:{region:"us-east-1",appsyncUrl:"https://jwhyxq4sgrgcdosewp5k4ns5ca.appsync-api.us-east-1.amazonaws.com/graphql",cognitoUserPoolId:"us-east-1_mNRO0j5og",cognitoClientId:"5p04dbc9ojptc5r8n7605fg78f",cognitoDomain:"codevibe-production.auth.us-east-1.amazoncognito.com"},keychain:{serviceName:"ai.quantiya.app.codevibe"},server:{port:3456,host:"127.0.0.1",dynamicPort:!0},claude:{command:"claude",defaultTimeout:6e4},codex:{command:"codex",defaultTimeout:6e4,sessionsDir:W.default.join(V.default.homedir(),".codex","sessions"),approvalTimeoutMs:5e3},gemini:{command:"gemini",defaultTimeout:6e4,transcriptDir:W.default.join(V.default.homedir(),".gemini","tmp")}}},Z=null,$e=!1});var _=E(()=>{"use strict";Le()});var te,Ue,T,me,ut,N,g,_e=E(()=>{"use strict";te=y(require("os")),Ue=require("uuid");Ne();Q();_();L();T=class extends Error{constructor(e){super(e),this.name="KeychainError"}},me="device-identity",ut="tokens-",N=class r{constructor(){this.deviceIdentity=null;this.sessionKeyCache=new Map;this.isRegistered=!1;this._serviceName=null}get serviceName(){return this._serviceName||(this._serviceName=f().keychain.serviceName),this._serviceName}static getInstance(){return r.instance||(r.instance=new r),r.instance}async getDeviceIdentity(){if(this.deviceIdentity)return this.deviceIdentity;let e=await le(this.serviceName,me);return e?(this.deviceIdentity=JSON.parse(e),c.info(`[KeychainManager] Loaded device identity: ${this.deviceIdentity.deviceId}`),this.deviceIdentity):null}async setDeviceIdentity(e){try{await pe(this.serviceName,me,JSON.stringify(e)),this.deviceIdentity=e,c.info(`[KeychainManager] Saved device identity: ${e.deviceId}`)}catch(t){throw c.error(`[KeychainManager] Failed to save device identity: ${t}`),new T(`Failed to save device identity: ${t}`)}}async getOrCreateDeviceIdentity(){let e=await this.getDeviceIdentity();if(e)return e;let t=b.generateKeyPair();return e={deviceId:(0,Ue.v4)().toUpperCase(),privateKey:t.privateKey,publicKey:t.publicKey,createdAt:new Date().toISOString()},await this.setDeviceIdentity(e),c.info(`[KeychainManager] Generated new device identity: ${e.deviceId}`),e}async getDeviceId(){return(await this.getOrCreateDeviceIdentity()).deviceId}async getDevicePublicKey(){return(await this.getOrCreateDeviceIdentity()).publicKey}async getDevicePrivateKey(){return(await this.getOrCreateDeviceIdentity()).privateKey}async hasDeviceIdentity(){return await this.getDeviceIdentity()!==null}async deleteDeviceIdentity(){try{await ue(this.serviceName,me),this.deviceIdentity=null,this.sessionKeyCache.clear(),this.isRegistered=!1,c.info("[KeychainManager] Deleted device identity")}catch(e){throw c.error(`[KeychainManager] Failed to delete device identity: ${e}`),new T(`Failed to delete device identity: ${e}`)}}getTokenAccount(e){return`${ut}${e}`}async getTokens(e="production"){let t=await le(this.serviceName,this.getTokenAccount(e));if(!t)return null;let n=JSON.parse(t);return c.debug(`[KeychainManager] Loaded tokens for ${e}`),n}async setTokens(e,t="production"){try{await pe(this.serviceName,this.getTokenAccount(t),JSON.stringify(e)),c.info(`[KeychainManager] Saved tokens for ${t}`,{userId:e.userId,email:e.email})}catch(n){throw c.error(`[KeychainManager] Failed to save tokens: ${n}`),new T(`Failed to save tokens: ${n}`)}}async deleteTokens(e="production"){try{let t=await ue(this.serviceName,this.getTokenAccount(e));return t&&c.info(`[KeychainManager] Deleted tokens for ${e}`),t}catch(t){return c.error(`[KeychainManager] Failed to delete tokens: ${t}`),!1}}isTokenExpired(e){return Date.now()>=e.expiresAt-3e5}async getSessionKey(e,t){let n=this.sessionKeyCache.get(e);if(n)return n;if(!t||t.length===0)return null;let s=await this.getDeviceId(),i=t.find(l=>l.deviceId===s);if(!i)return c.warn(`[KeychainManager] Device ${s} not found in encryptedKeys`),null;let o=await this.getDevicePrivateKey(),a=b.decryptSessionKey(i,o);return this.sessionKeyCache.set(e,a),c.info(`[KeychainManager] Decrypted and cached session key for ${e}`),a}createSessionKey(e){let t=b.generateSessionKey(),n=e.map(s=>{let i=b.encryptSessionKey(t,s.publicKey);return{deviceId:s.deviceId,encryptedKey:i.encryptedKey,ephemeralPublicKey:i.ephemeralPublicKey}});return c.info(`[KeychainManager] Created session key for ${e.length} devices`),{sessionKey:t,encryptedKeys:n}}cacheSessionKey(e,t){this.sessionKeyCache.set(e,t)}clearSessionKey(e){this.sessionKeyCache.delete(e)}clearAllSessionKeys(){this.sessionKeyCache.clear()}getIsRegistered(){return this.isRegistered}setIsRegistered(e){this.isRegistered=e}getDeviceName(){return te.hostname()||"CLI Client"}getDevicePlatform(){let e=te.platform();return e==="darwin"?"MACOS":e==="linux"?"LINUX":e==="win32"?"WINDOWS":"CLI"}async clearAllData(){await this.deleteDeviceIdentity(),await this.deleteTokens("development"),await this.deleteTokens("production"),this.sessionKeyCache.clear(),this.isRegistered=!1,c.info("[KeychainManager] Cleared all data")}},g=N.getInstance()});var Me={};Te(Me,{KeychainError:()=>T,KeychainManager:()=>N,keychainManager:()=>g});var M=E(()=>{"use strict";_e()});var Ct={};Te(Ct,{AgentType:()=>Ve,AppSyncClient:()=>J,AuthService:()=>q,CryptoError:()=>K,CryptoService:()=>U,DeliveryStatus:()=>He,ENCRYPTION_VERSION:()=>ge,EventSource:()=>ye,EventType:()=>qe,KeychainError:()=>T,KeychainManager:()=>N,Logger:()=>D,SessionStatus:()=>fe,authService:()=>x,createLogger:()=>se,cryptoService:()=>b,getConfig:()=>f,getEnvironment:()=>h,keychainManager:()=>g,loadConfig:()=>ee,logger:()=>c,mutations:()=>k,normalizeSnapshot:()=>Ee,parseInteractivePrompt:()=>Ye,prepareSessionEncryption:()=>re,queries:()=>R,resumeOrCreateSession:()=>Ie,runAuthCli:()=>ne,subscriptions:()=>j});module.exports=st(Ct);M();Q();var he=y(require("ws")),ve=require("uuid");_();L();M();var Be=y(require("dns")),Fe=y(require("fs"));if(gt())try{Be.setDefaultResultOrder("ipv4first")}catch{}function gt(){if(process.platform!=="linux")return!1;try{let r=Fe.readFileSync("/proc/sys/kernel/osrelease","utf8");return/microsoft|wsl/i.test(r)}catch{return!1}}async function B(r,e,t){try{return await fetch(r,e)}catch(n){let s=n?.cause?.code,i=n?.cause?.message,o=s||i||n?.message||"unknown",a=mt(s),l=t?`${t}: `:"",p=`Node ${process.version} on ${process.platform}`,m=[`${l}Cannot reach ${r}`,` Underlying error: ${o}`];a&&m.push(` Suggested fix: ${a}`),m.push(` Platform: ${p}`);let u=new Error(m.join(`
|
|
1
|
+
"use strict";var Ze=Object.create;var Y=Object.defineProperty;var et=Object.getOwnPropertyDescriptor;var tt=Object.getOwnPropertyNames;var nt=Object.getPrototypeOf,rt=Object.prototype.hasOwnProperty;var E=(r,e)=>()=>(r&&(e=r(r=0)),e);var Te=(r,e)=>{for(var t in e)Y(r,t,{get:e[t],enumerable:!0})},xe=(r,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of tt(e))!rt.call(r,i)&&i!==t&&Y(r,i,{get:()=>e[i],enumerable:!(n=et(e,i))||n.enumerable});return r};var y=(r,e,t)=>(t=r!=null?Ze(nt(r)):{},xe(e||!r||!r.__esModule?Y(t,"default",{value:r,enumerable:!0}):t,r)),it=r=>xe(Y({},"__esModule",{value:!0}),r);function st(r,e){if(e instanceof Error){let t={name:e.name,message:e.message};e.stack&&(t.stack=e.stack);for(let n of Object.keys(e))n in t||(t[n]=e[n]);return t}return e}function ie(r){return new D(r)}var $,G,Ce,Ae,D,c,De=E(()=>{"use strict";$=y(require("fs")),G=y(require("path")),Ce=y(require("os")),Ae={debug:0,info:1,warn:2,error:3};D=class{constructor(e){this.name=e.name,this.logFile=e.logFile,this.level=e.level||"info",this.enableConsole=e.console??!1,this.logFile&&this.ensureLogDir()}ensureLogDir(){if(this.logFile){let e=G.dirname(this.logFile);$.existsSync(e)||$.mkdirSync(e,{recursive:!0})}}shouldLog(e){return Ae[e]>=Ae[this.level]}formatMessage(e,t,n){let i=new Date().toISOString(),s=e.toUpperCase().padEnd(5),o=`[${i}] [${s}] [${this.name}] ${t}`;return n!==void 0&&(n instanceof Error?(o+=` ${n.name}: ${n.message}`,n.stack&&(o+=`
|
|
2
|
+
${n.stack}`)):typeof n=="object"?o+=` ${JSON.stringify(n,st)}`:o+=` ${n}`),o}log(e,t,n){if(!this.shouldLog(e))return;let i=this.formatMessage(e,t,n);if(this.logFile)try{$.appendFileSync(this.logFile,i+`
|
|
3
|
+
`)}catch{}if(this.enableConsole)switch(e){case"error":console.error(i);break;case"warn":console.warn(i);break;default:console.log(i)}}debug(e,t){this.log("debug",e,t)}info(e,t){this.log("info",e,t)}warn(e,t){this.log("warn",e,t)}error(e,t){this.log("error",e,t)}setLevel(e){this.level=e}};c=new D({name:"codevibe-core",logFile:G.join(Ce.tmpdir(),"codevibe-core.log"),level:"info"})});var L=E(()=>{"use strict";De()});function ot(r){for(let e of r)try{process.stderr.write(e+`
|
|
4
|
+
`)}catch{}}function at(){se=ae.join(Pe.homedir(),".codevibe");try{A.mkdirSync(se,{recursive:!0,mode:448})}catch{}P="file"}function ct(){if(P!==null||oe!==null)return;let optedIn=process.env.CODEVIBE_ALLOW_FILE_KEYCHAIN==="1";if(optedIn){ot(["","\u26A0 CodeVibe: file-based credential storage selected (CODEVIBE_ALLOW_FILE_KEYCHAIN=1).","\u26A0 Location: ~/.codevibe/ (directory 0700, files 0600)","\u26A0 Trust level: equivalent to ~/.ssh/id_rsa \u2014 weaker than OS keyring.","\u26A0 To use the OS keyring instead, unset CODEVIBE_ALLOW_FILE_KEYCHAIN and","\u26A0 install libsecret-1-0 + a running keyring daemon (Linux) or use the","\u26A0 native Keychain (macOS) / Credential Manager (Windows).",""]),c.warn("[keychain-backend] Using file-based storage at ~/.codevibe (CODEVIBE_ALLOW_FILE_KEYCHAIN=1 explicit opt-in)"),at();return}let keytarLoadError=null;try{let nodeRequire=eval("require");I=nodeRequire("keytar")}catch(r){keytarLoadError=r instanceof Error?r.message:String(r),I=null}if(I){P="keytar",c.info("[keychain-backend] Using keytar (OS-native keyring)");return}oe=new X(["CodeVibe could not load the OS-native keyring (keytar).",`Reason: ${keytarLoadError??"unknown"}`,"","Options to fix this:"," 1. (Linux) Install libsecret and a keyring daemon:"," sudo apt install libsecret-1-0 gnome-keyring"," Then unlock the keyring for your user session.",""," 2. (Headless / CI / Docker) Opt in to file-based credential"," storage at ~/.codevibe/ (0600 files). This is equivalent"," in trust to ~/.ssh/id_rsa \u2014 not the OS keyring:"," export CODEVIBE_ALLOW_FILE_KEYCHAIN=1"].join(`
|
|
5
|
+
`))}function dt(r){return r.replace(/[^a-zA-Z0-9._-]/g,"_")}function Ke(r){return ae.join(se,`${dt(r)}.json`)}function ce(r){try{let e=A.readFileSync(Ke(r),"utf-8"),t=JSON.parse(e);return t&&typeof t=="object"?t:{}}catch{return{}}}function Oe(r,e){let t=Ke(r);A.writeFileSync(t,JSON.stringify(e,null,2),{mode:384});try{A.chmodSync(t,384)}catch{}}function de(){if(ct(),P===null)throw oe??new X("Keychain backend not initialized")}async function le(r,e){return de(),P==="keytar"&&I?I.getPassword(r,e):ce(r)[e]??null}async function pe(r,e,t){if(de(),P==="keytar"&&I){await I.setPassword(r,e,t);return}let n=ce(r);n[e]=t,Oe(r,n)}async function ue(r,e){if(de(),P==="keytar"&&I)return I.deletePassword(r,e);let t=ce(r);return e in t?(delete t[e],Oe(r,t),!0):!1}var Pe,ae,A,X,P,I,se,oe,Ne=E(()=>{"use strict";Pe=y(require("os")),ae=y(require("path")),A=y(require("fs"));L();X=class extends Error{constructor(e){super(e),this.name="KeychainBackendUnavailableError"}},P=null,I=null,se="",oe=null});var v,K,ge,pt,U,b,Re=E(()=>{"use strict";v=y(require("crypto")),K=class extends Error{constructor(e){super(e),this.name="CryptoError"}},ge=1,pt="CodeVibe E2E v1",U=class r{constructor(){}static getInstance(){return r.instance||(r.instance=new r),r.instance}generateKeyPair(){let e=v.createECDH("prime256v1");e.generateKeys();let n=e.getPublicKey().subarray(1).toString("base64");return{privateKey:e.getPrivateKey().toString("base64"),publicKey:n}}generateSessionKey(){return v.randomBytes(32).toString("base64")}deriveSharedKey(e,t){try{let n=v.createECDH("prime256v1"),i=Buffer.from(e,"base64");n.setPrivateKey(i);let s=Buffer.concat([Buffer.from([4]),Buffer.from(t,"base64")]),o=n.computeSecret(s),a=v.hkdfSync("sha256",o,Buffer.alloc(0),Buffer.from(pt,"utf8"),32);return Buffer.from(a)}catch(n){throw new K(`Failed to derive shared key: ${n}`)}}encryptSessionKey(e,t){let n=this.generateKeyPair(),i=this.deriveSharedKey(n.privateKey,t),s=Buffer.from(e,"base64");return{encryptedKey:this.encrypt(s,i).toString("base64"),ephemeralPublicKey:n.publicKey}}decryptSessionKey(e,t){let n=this.deriveSharedKey(t,e.ephemeralPublicKey),i=Buffer.from(e.encryptedKey,"base64");return this.decrypt(i,n).toString("base64")}encryptContent(e,t){let n=Buffer.from(t,"base64"),i=Buffer.from(e,"utf8");return this.encrypt(i,n).toString("base64")}decryptContent(e,t){let n=Buffer.from(t,"base64"),i=Buffer.from(e,"base64");return this.decrypt(i,n).toString("utf8")}encryptMetadata(e,t){let n=JSON.stringify(e);return this.encryptContent(n,t)}decryptMetadata(e,t){let n=this.decryptContent(e,t);return JSON.parse(n)}encryptData(e,t){let n=Buffer.from(t,"base64");return this.encrypt(e,n)}decryptData(e,t){let n=Buffer.from(t,"base64");return this.decrypt(e,n)}encrypt(e,t){let n=v.randomBytes(12),i=v.createCipheriv("aes-256-gcm",t,n),s=Buffer.concat([i.update(e),i.final()]),o=i.getAuthTag();return Buffer.concat([n,s,o])}decrypt(e,t){let n=e.subarray(0,12),i=e.subarray(e.length-16),s=e.subarray(12,e.length-16),o=v.createDecipheriv("aes-256-gcm",t,n);o.setAuthTag(i);try{return Buffer.concat([o.update(s),o.final()])}catch{throw new K("Decryption failed: Invalid ciphertext or authentication tag")}}serializePrivateKey(e){return e}deserializePrivateKey(e){return e}},b=U.getInstance()});var Q=E(()=>{"use strict";Re()});function h(){let r=process.env.ENVIRONMENT;return r==="development"||r==="production"?r:"production"}function ee(r){let e=r||h();return Z={...O[e],aws:{...O[e].aws,region:process.env.AWS_REGION||O[e].aws.region,appsyncUrl:process.env.APPSYNC_URL||O[e].aws.appsyncUrl,cognitoUserPoolId:process.env.COGNITO_USER_POOL_ID||O[e].aws.cognitoUserPoolId,cognitoClientId:process.env.COGNITO_CLIENT_ID||O[e].aws.cognitoClientId,cognitoDomain:process.env.COGNITO_DOMAIN||O[e].aws.cognitoDomain}},$e=!0,Z}function f(){return(!$e||!Z)&&ee(),Z}var V,W,O,Z,$e,Le=E(()=>{"use strict";V=y(require("os")),W=y(require("path")),O={development:{environment:"development",aws:{region:"us-east-1",appsyncUrl:"https://te6rjr37sbfpjc4fiunmb2tgy4.appsync-api.us-east-1.amazonaws.com/graphql",cognitoUserPoolId:"us-east-1_yVwWDPvvJ",cognitoClientId:"e9r5apv6v5uui3l928r2ris0r",cognitoDomain:"codevibe-development.auth.us-east-1.amazoncognito.com"},keychain:{serviceName:"ai.quantiya.app.codevibe"},server:{port:3456,host:"127.0.0.1",dynamicPort:!0},claude:{command:"claude",defaultTimeout:6e4},codex:{command:"codex",defaultTimeout:6e4,sessionsDir:W.default.join(V.default.homedir(),".codex","sessions"),approvalTimeoutMs:5e3},gemini:{command:"gemini",defaultTimeout:6e4,transcriptDir:W.default.join(V.default.homedir(),".gemini","tmp")}},production:{environment:"production",aws:{region:"us-east-1",appsyncUrl:"https://jwhyxq4sgrgcdosewp5k4ns5ca.appsync-api.us-east-1.amazonaws.com/graphql",cognitoUserPoolId:"us-east-1_mNRO0j5og",cognitoClientId:"5p04dbc9ojptc5r8n7605fg78f",cognitoDomain:"codevibe-production.auth.us-east-1.amazoncognito.com"},keychain:{serviceName:"ai.quantiya.app.codevibe"},server:{port:3456,host:"127.0.0.1",dynamicPort:!0},claude:{command:"claude",defaultTimeout:6e4},codex:{command:"codex",defaultTimeout:6e4,sessionsDir:W.default.join(V.default.homedir(),".codex","sessions"),approvalTimeoutMs:5e3},gemini:{command:"gemini",defaultTimeout:6e4,transcriptDir:W.default.join(V.default.homedir(),".gemini","tmp")}}},Z=null,$e=!1});var _=E(()=>{"use strict";Le()});var te,Ue,T,me,ut,N,g,_e=E(()=>{"use strict";te=y(require("os")),Ue=require("uuid");Ne();Q();_();L();T=class extends Error{constructor(e){super(e),this.name="KeychainError"}},me="device-identity",ut="tokens-",N=class r{constructor(){this.deviceIdentity=null;this.sessionKeyCache=new Map;this.isRegistered=!1;this._serviceName=null}get serviceName(){return this._serviceName||(this._serviceName=f().keychain.serviceName),this._serviceName}static getInstance(){return r.instance||(r.instance=new r),r.instance}async getDeviceIdentity(){if(this.deviceIdentity)return this.deviceIdentity;let e=await le(this.serviceName,me);return e?(this.deviceIdentity=JSON.parse(e),c.info(`[KeychainManager] Loaded device identity: ${this.deviceIdentity.deviceId}`),this.deviceIdentity):null}async setDeviceIdentity(e){try{await pe(this.serviceName,me,JSON.stringify(e)),this.deviceIdentity=e,c.info(`[KeychainManager] Saved device identity: ${e.deviceId}`)}catch(t){throw c.error(`[KeychainManager] Failed to save device identity: ${t}`),new T(`Failed to save device identity: ${t}`)}}async getOrCreateDeviceIdentity(){let e=await this.getDeviceIdentity();if(e)return e;let t=b.generateKeyPair();return e={deviceId:(0,Ue.v4)().toUpperCase(),privateKey:t.privateKey,publicKey:t.publicKey,createdAt:new Date().toISOString()},await this.setDeviceIdentity(e),c.info(`[KeychainManager] Generated new device identity: ${e.deviceId}`),e}async getDeviceId(){return(await this.getOrCreateDeviceIdentity()).deviceId}async getDevicePublicKey(){return(await this.getOrCreateDeviceIdentity()).publicKey}async getDevicePrivateKey(){return(await this.getOrCreateDeviceIdentity()).privateKey}async hasDeviceIdentity(){return await this.getDeviceIdentity()!==null}async deleteDeviceIdentity(){try{await ue(this.serviceName,me),this.deviceIdentity=null,this.sessionKeyCache.clear(),this.isRegistered=!1,c.info("[KeychainManager] Deleted device identity")}catch(e){throw c.error(`[KeychainManager] Failed to delete device identity: ${e}`),new T(`Failed to delete device identity: ${e}`)}}getTokenAccount(e){return`${ut}${e}`}async getTokens(e="production"){let t=await le(this.serviceName,this.getTokenAccount(e));if(!t)return null;let n=JSON.parse(t);return c.debug(`[KeychainManager] Loaded tokens for ${e}`),n}async setTokens(e,t="production"){try{await pe(this.serviceName,this.getTokenAccount(t),JSON.stringify(e)),c.info(`[KeychainManager] Saved tokens for ${t}`,{userId:e.userId,email:e.email})}catch(n){throw c.error(`[KeychainManager] Failed to save tokens: ${n}`),new T(`Failed to save tokens: ${n}`)}}async deleteTokens(e="production"){try{let t=await ue(this.serviceName,this.getTokenAccount(e));return t&&c.info(`[KeychainManager] Deleted tokens for ${e}`),t}catch(t){return c.error(`[KeychainManager] Failed to delete tokens: ${t}`),!1}}isTokenExpired(e){return Date.now()>=e.expiresAt-3e5}async getSessionKey(e,t){let n=this.sessionKeyCache.get(e);if(n)return n;if(!t||t.length===0)return null;let i=await this.getDeviceId(),s=t.find(l=>l.deviceId===i);if(!s)return c.warn(`[KeychainManager] Device ${i} not found in encryptedKeys`),null;let o=await this.getDevicePrivateKey(),a=b.decryptSessionKey(s,o);return this.sessionKeyCache.set(e,a),c.info(`[KeychainManager] Decrypted and cached session key for ${e}`),a}createSessionKey(e){let t=b.generateSessionKey(),n=e.map(i=>{let s=b.encryptSessionKey(t,i.publicKey);return{deviceId:i.deviceId,encryptedKey:s.encryptedKey,ephemeralPublicKey:s.ephemeralPublicKey}});return c.info(`[KeychainManager] Created session key for ${e.length} devices`),{sessionKey:t,encryptedKeys:n}}cacheSessionKey(e,t){this.sessionKeyCache.set(e,t)}clearSessionKey(e){this.sessionKeyCache.delete(e)}clearAllSessionKeys(){this.sessionKeyCache.clear()}getIsRegistered(){return this.isRegistered}setIsRegistered(e){this.isRegistered=e}getDeviceName(){return te.hostname()||"CLI Client"}getDevicePlatform(){let e=te.platform();return e==="darwin"?"MACOS":e==="linux"?"LINUX":e==="win32"?"WINDOWS":"CLI"}async clearAllData(){await this.deleteDeviceIdentity(),await this.deleteTokens("development"),await this.deleteTokens("production"),this.sessionKeyCache.clear(),this.isRegistered=!1,c.info("[KeychainManager] Cleared all data")}},g=N.getInstance()});var Me={};Te(Me,{KeychainError:()=>T,KeychainManager:()=>N,keychainManager:()=>g});var M=E(()=>{"use strict";_e()});var Ct={};Te(Ct,{AgentType:()=>Ve,AppSyncClient:()=>J,AuthService:()=>q,CryptoError:()=>K,CryptoService:()=>U,DeliveryStatus:()=>He,ENCRYPTION_VERSION:()=>ge,EventSource:()=>ye,EventType:()=>qe,KeychainError:()=>T,KeychainManager:()=>N,Logger:()=>D,SessionStatus:()=>fe,authService:()=>x,createLogger:()=>ie,cryptoService:()=>b,getConfig:()=>f,getEnvironment:()=>h,keychainManager:()=>g,loadConfig:()=>ee,logger:()=>c,mutations:()=>k,normalizeSnapshot:()=>Ee,parseInteractivePrompt:()=>Ye,prepareSessionEncryption:()=>re,queries:()=>R,resumeOrCreateSession:()=>Ie,runAuthCli:()=>ne,subscriptions:()=>j});module.exports=it(Ct);M();Q();var he=y(require("ws")),ve=require("uuid");_();L();M();var Be=y(require("dns")),Fe=y(require("fs"));if(gt())try{Be.setDefaultResultOrder("ipv4first")}catch{}function gt(){if(process.platform!=="linux")return!1;try{let r=Fe.readFileSync("/proc/sys/kernel/osrelease","utf8");return/microsoft|wsl/i.test(r)}catch{return!1}}async function B(r,e,t){try{return await fetch(r,e)}catch(n){let i=n?.cause?.code,s=n?.cause?.message,o=i||s||n?.message||"unknown",a=mt(i),l=t?`${t}: `:"",p=`Node ${process.version} on ${process.platform}`,m=[`${l}Cannot reach ${r}`,` Underlying error: ${o}`];a&&m.push(` Suggested fix: ${a}`),m.push(` Platform: ${p}`);let u=new Error(m.join(`
|
|
6
6
|
`));throw u.cause=n,u}}function mt(r){if(!r)return null;switch(r){case"ENOTFOUND":case"EAI_AGAIN":return'DNS resolution failed. On WSL Ubuntu, check /etc/resolv.conf, or try running with NODE_OPTIONS="--dns-result-order=ipv4first".';case"ETIMEDOUT":case"ECONNREFUSED":case"ECONNRESET":case"EHOSTUNREACH":case"ENETUNREACH":return`Network unreachable. On WSL Ubuntu, try NODE_OPTIONS="--dns-result-order=ipv4first" (WSL's IPv6 is often broken). If behind a corporate proxy, set HTTPS_PROXY.`;case"CERT_HAS_EXPIRED":case"CERT_NOT_YET_VALID":return"TLS certificate time error \u2014 likely system clock drift. On WSL, run `sudo hwclock -s`, or shut down WSL from PowerShell with `wsl --shutdown` and restart.";case"UNABLE_TO_GET_ISSUER_CERT_LOCALLY":case"SELF_SIGNED_CERT_IN_CHAIN":case"UNABLE_TO_VERIFY_LEAF_SIGNATURE":case"DEPTH_ZERO_SELF_SIGNED_CERT":return"Corporate HTTPS proxy detected \u2014 the TLS cert is not trusted by Node. Set NODE_EXTRA_CA_CERTS=/path/to/corporate-ca.pem, or configure HTTPS_PROXY if a proxy is required.";default:return null}}var R={getSession:`
|
|
7
7
|
query GetSession($sessionId: ID!) {
|
|
8
8
|
getSession(sessionId: $sessionId) {
|
|
@@ -166,7 +166,7 @@ ${n.stack}`)):typeof n=="object"?o+=` ${JSON.stringify(n,it)}`:o+=` ${n}`),o}log
|
|
|
166
166
|
isEncrypted
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
|
-
`};var qe=(a=>(a.USER_PROMPT="USER_PROMPT",a.ASSISTANT_RESPONSE="ASSISTANT_RESPONSE",a.TOOL_USE="TOOL_USE",a.NOTIFICATION="NOTIFICATION",a.INTERACTIVE_PROMPT="INTERACTIVE_PROMPT",a.PROMPT_RESPONSE="PROMPT_RESPONSE",a.REASONING="REASONING",a))(qe||{}),ye=(t=>(t.DESKTOP="DESKTOP",t.MOBILE="MOBILE",t))(ye||{}),He=(n=>(n.SENT="SENT",n.DELIVERED="DELIVERED",n.EXECUTED="EXECUTED",n))(He||{});var fe=(n=>(n.ACTIVE="ACTIVE",n.INACTIVE="INACTIVE",n.PAUSED="PAUSED",n))(fe||{}),Ve=(n=>(n.CLAUDE="CLAUDE",n.GEMINI="GEMINI",n.CODEX="CODEX",n))(Ve||{});var F={urgentMaxAttempts:10,baseDelayMs:1e3,maxDelayMs:6e4,backoffMultiplier:2,persistentDelayMs:300*1e3},J=class{constructor(){this.authenticated=!1;this.currentUserId=null;this.currentEmail=null;this.tokens=null;this.activeSubscriptions=new Map;this.heartbeatTimers=new Map;this.environment=h(),c.info("[AppSyncClient] Initialized",{environment:this.environment})}getCurrentUserId(){if(!this.currentUserId)throw new Error("Not authenticated. Call authenticateWithStoredTokens() first.");return this.currentUserId}getCurrentUserEmail(){return this.currentEmail}async authenticateWithStoredTokens(){try{let e=await g.getTokens(this.environment);if(!e)return c.debug("[AppSyncClient] No stored tokens found"),!1;if(c.info("[AppSyncClient] Found stored OAuth tokens",{userId:e.userId,email:e.email,expired:g.isTokenExpired(e)}),g.isTokenExpired(e)){if(c.info("[AppSyncClient] Tokens expired, attempting refresh..."),!await this.refreshTokens(e))return c.warn("[AppSyncClient] Token refresh failed"),!1}else this.tokens=e;return this.currentUserId=this.tokens.userId,this.currentEmail=this.tokens.email,this.authenticated=!0,c.info("[AppSyncClient] Authenticated successfully",{userId:this.currentUserId,email:this.currentEmail}),!0}catch(e){return c.error("[AppSyncClient] Authentication failed:",e),!1}}async refreshTokens(e){try{let t=f(),n=`https://${t.aws.cognitoDomain}/oauth2/token`,s=new URLSearchParams({grant_type:"refresh_token",client_id:t.aws.cognitoClientId,refresh_token:e.refreshToken}),i=await B(n,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:s.toString()},"Token refresh");if(!i.ok)return c.error("[AppSyncClient] Token refresh failed",{status:i.status}),!1;let o=await i.json(),a={...e,accessToken:o.access_token,idToken:o.id_token,expiresAt:Date.now()+o.expires_in*1e3};return await g.setTokens(a,this.environment),this.tokens=a,c.info("[AppSyncClient] Tokens refreshed",{expiresAt:new Date(a.expiresAt).toISOString()}),!0}catch(t){return c.error("[AppSyncClient] Token refresh error:",t),!1}}isAuthenticated(){return this.authenticated}signOut(){this.authenticated=!1,this.tokens=null,this.currentUserId=null,this.currentEmail=null,this.cleanupSubscriptions(),c.info("[AppSyncClient] Signed out")}async graphqlRequest(e,t,n=!1){let s=f();if(!this.tokens?.idToken)throw new Error('Not authenticated. Run "codevibe login" first.');let i={"Content-Type":"application/json",Authorization:this.tokens.idToken},o=await B(s.aws.appsyncUrl,{method:"POST",headers:i,body:JSON.stringify({query:e,variables:t})},"AppSync GraphQL request"),a=await o.json();if(o.status===401&&!n&&this.tokens){if(c.info("[AppSyncClient] 401 Unauthorized, refreshing token..."),await this.refreshTokens(this.tokens))return this.graphqlRequest(e,t,!0);throw new Error("Token expired and refresh failed")}if(!o.ok)throw new Error(`GraphQL request failed: ${o.status}`);if(a.errors?.length)throw new Error(`GraphQL error: ${a.errors[0].message}`);return a}async createSession(e){let t={...e,metadata:e.metadata?JSON.stringify(e.metadata):void 0},n=await this.graphqlRequest(k.createSession,{input:t});return c.info("[AppSyncClient] Session created",{sessionId:n.data.createSession.sessionId}),n.data.createSession}async updateSession(e){let t={...e,metadata:e.metadata?JSON.stringify(e.metadata):void 0},n=await this.graphqlRequest(k.updateSession,{input:t});return c.debug("[AppSyncClient] Session updated",{sessionId:n.data.updateSession.sessionId}),n.data.updateSession}async getSession(e){return(await this.graphqlRequest(R.getSession,{sessionId:e})).data.getSession}async createEvent(e){let t={...e,metadata:e.metadata?JSON.stringify(e.metadata):void 0},n=await this.graphqlRequest(k.createEvent,{input:t});return c.debug("[AppSyncClient] Event created",{eventId:n.data.createEvent.eventId,type:n.data.createEvent.type}),n.data.createEvent}async updateEventStatus(e){return(await this.graphqlRequest(k.updateEventStatus,{input:e})).data.updateEventStatus}async listEvents(e,t,n){return(await this.graphqlRequest(R.listEvents,{sessionId:e,source:t,limit:n})).data.listEvents.items}async listUserDeviceKeys(){return(await this.graphqlRequest(R.listUserDeviceKeys,{})).data.listUserDeviceKeys||[]}async registerDeviceKey(e,t,n,s){let i={deviceId:e,publicKey:t,platform:n,deviceName:s};await this.graphqlRequest(k.registerDeviceKey,{input:i}),c.info("[AppSyncClient] Device key registered",{deviceId:e,platform:n})}async getAttachmentDownloadUrl(e){return(await this.graphqlRequest(k.getAttachmentDownloadUrl,{s3Key:e})).data.getAttachmentDownloadUrl}subscribeToEvents(e,t,n){c.info("[AppSyncClient] Subscribing to events",{sessionId:e});let s=this.activeSubscriptions.get(e);s&&(this.cleanupSubscriptionState(s),this.activeSubscriptions.delete(e));let i={ws:null,subscriptionId:(0,ve.v4)(),sessionId:e,onEvent:t,onError:n,reconnectAttempts:0,isReconnecting:!1};return this.activeSubscriptions.set(e,i),this.createSubscription(i),()=>{this.cleanupSubscriptionState(i),this.activeSubscriptions.delete(e)}}buildRealtimeUrl(){let e=f(),t=e.aws.appsyncUrl.replace("https://","wss://").replace("appsync-api","appsync-realtime-api"),n={host:new URL(e.aws.appsyncUrl).host};this.tokens?.idToken&&(n.Authorization=this.tokens.idToken);let s=Buffer.from(JSON.stringify(n)).toString("base64"),i=Buffer.from(JSON.stringify({})).toString("base64");return`${t}?header=${s}&payload=${i}`}createSubscription(e){let{sessionId:t,subscriptionId:n,onEvent:s,onError:i}=e;try{let o=this.buildRealtimeUrl(),a=new he.default(o,["graphql-ws"]);a.on("open",()=>{c.info("[AppSyncClient] WebSocket connected",{sessionId:t}),a.send(JSON.stringify({type:"connection_init"}))}),a.on("message",l=>{try{let p=JSON.parse(l.toString());switch(p.type){case"connection_ack":this.sendSubscriptionStart(a,e);break;case"start_ack":c.info("[AppSyncClient] Subscription started",{sessionId:t}),e.isReconnecting=!1,e.reconnectAttempts=0,this.startHeartbeat(t);break;case"data":this.resetKeepAliveTimer(e);let m=p.payload?.data?.onEventCreated;m&&m.source==="MOBILE"&&s(m);break;case"ka":this.resetKeepAliveTimer(e);break;case"error":let u=p.payload?.errors?.[0]?.message||"Unknown error";this.handleSubscriptionError(e,new Error(u));break}}catch(p){c.error("[AppSyncClient] Failed to parse message",{error:p})}}),a.on("error",l=>{c.error("[AppSyncClient] WebSocket error",{sessionId:t,error:l.message}),this.handleSubscriptionError(e,l)}),a.on("close",(l,p)=>{c.info("[AppSyncClient] WebSocket closed",{sessionId:t,code:l}),e.keepAliveTimer&&clearTimeout(e.keepAliveTimer),this.activeSubscriptions.has(t)&&this.handleSubscriptionError(e,new Error(`WebSocket closed: ${l}`))}),e.ws=a,this.resetKeepAliveTimer(e)}catch(o){this.handleSubscriptionError(e,o)}}sendSubscriptionStart(e,t){let n=f(),{sessionId:s,subscriptionId:i}=t,o={host:new URL(n.aws.appsyncUrl).host};this.tokens?.idToken&&(o.Authorization=this.tokens.idToken),e.send(JSON.stringify({id:i,type:"start",payload:{data:JSON.stringify({query:j.onEventCreated,variables:{sessionId:s}}),extensions:{authorization:o}}}))}resetKeepAliveTimer(e){e.keepAliveTimer&&clearTimeout(e.keepAliveTimer),e.keepAliveTimer=setTimeout(()=>{this.handleSubscriptionError(e,new Error("Keep-alive timeout"))},300*1e3)}handleSubscriptionError(e,t){let{sessionId:n,onError:s}=e;if(e.isReconnecting||!this.activeSubscriptions.has(n))return;e.isReconnecting=!0,e.reconnectAttempts++,this.stopHeartbeat(n);let i=e.reconnectAttempts<=F.urgentMaxAttempts,o;if(i?o=Math.min(F.baseDelayMs*Math.pow(F.backoffMultiplier,e.reconnectAttempts-1),F.maxDelayMs):(o=F.persistentDelayMs,e.reconnectAttempts===F.urgentMaxAttempts+1&&c.info("[AppSyncClient] Switching to persistent reconnect (every 5min)",{sessionId:n})),c.info("[AppSyncClient] Scheduling reconnect",{sessionId:n,attempt:e.reconnectAttempts,phase:i?"urgent":"persistent",delayMs:o}),e.ws){try{e.ws.close(1e3)}catch{}e.ws=null}e.keepAliveTimer&&clearTimeout(e.keepAliveTimer),e.reconnectTimer=setTimeout(async()=>{if(e.isReconnecting=!1,!!this.activeSubscriptions.has(n)){try{let a=await g.getTokens(this.environment);a&&(g.isTokenExpired(a)?await this.refreshTokens(a)&&c.info("[AppSyncClient] Tokens refreshed before reconnect",{sessionId:n}):this.tokens=a)}catch{c.warn("[AppSyncClient] Token refresh failed before reconnect, using existing tokens",{sessionId:n})}e.subscriptionId=(0,ve.v4)(),this.createSubscription(e)}},o)}cleanupSubscriptionState(e){if(e.reconnectTimer&&clearTimeout(e.reconnectTimer),e.keepAliveTimer&&clearTimeout(e.keepAliveTimer),e.ws){try{e.ws.readyState===he.default.OPEN&&(e.ws.send(JSON.stringify({id:e.subscriptionId,type:"stop"})),e.ws.close(1e3))}catch{}e.ws=null}}startHeartbeat(e,t=120*1e3){this.stopHeartbeat(e),this.sendHeartbeat(e);let n=setInterval(()=>{this.sendHeartbeat(e)},t);this.heartbeatTimers.set(e,n),c.info("[AppSyncClient] Heartbeat started",{sessionId:e,intervalMs:t})}stopHeartbeat(e){let t=this.heartbeatTimers.get(e);t&&(clearInterval(t),this.heartbeatTimers.delete(e),c.info("[AppSyncClient] Heartbeat stopped",{sessionId:e}))}async sendHeartbeat(e){try{await this.updateSession({sessionId:e,lastHeartbeatAt:new Date().toISOString()}),c.debug("[AppSyncClient] Heartbeat sent",{sessionId:e})}catch(t){c.warn("[AppSyncClient] Heartbeat failed",{sessionId:e,error:t})}}cleanupSubscriptions(){this.activeSubscriptions.forEach(e=>{this.cleanupSubscriptionState(e)}),this.activeSubscriptions.clear(),this.heartbeatTimers.forEach(e=>clearInterval(e)),this.heartbeatTimers.clear()}};var We=y(require("crypto")),je=y(require("fs")),be=y(require("http")),Je=require("child_process");_();M();L();var z=8080,ze="/callback",Se=`http://localhost:${z}${ze}`,q=class r{constructor(){}static getInstance(){return r.instance||(r.instance=new r),r.instance}openBrowser(e){console.log(""),console.log("Opening your browser for sign-in..."),console.log("If your browser does not open automatically, visit this URL manually:"),console.log(` ${e}`),console.log("");let t=this.getBrowserCommands();this.tryBrowserCommand(t,e,0)}getBrowserCommands(){let e=process.platform;if(e==="darwin")return[{cmd:"open",fixedArgs:[]}];if(e==="win32")return[{cmd:"cmd",fixedArgs:["/c","start",""]}];let t=[];return this.isRunningInWSL()&&(t.push({cmd:"wslview",fixedArgs:[]}),t.push({cmd:"cmd.exe",fixedArgs:["/c","start",""]}),t.push({cmd:"powershell.exe",fixedArgs:["-NoProfile","-Command","Start-Process"]})),t.push({cmd:"xdg-open",fixedArgs:[]}),t}isRunningInWSL(){if(process.platform!=="linux")return!1;try{let e=je.readFileSync("/proc/sys/kernel/osrelease","utf8");return/microsoft|wsl/i.test(e)}catch{return!1}}tryBrowserCommand(e,t,n){if(n>=e.length){c.debug("[AuthService] No browser-opening command succeeded. User must open the sign-in URL manually (printed to stdout above).");return}let s=e[n],i=[...s.fixedArgs,t],o=!1,a=u=>{o||(o=!0,c.debug(`[AuthService] Browser command '${s.cmd}' ${u}; trying next fallback`),this.tryBrowserCommand(e,t,n+1))},l=u=>{o||(o=!0,c.debug(`[AuthService] Browser command '${s.cmd}' ${u}`))},p;try{p=(0,Je.spawn)(s.cmd,i,{detached:!0,stdio:"ignore"})}catch(u){a(`threw synchronously: ${u?.message||u}`);return}p.on("error",u=>{a(`failed to spawn: ${u?.message||u}`)}),p.on("exit",(u,C)=>{u===0?l("exited successfully"):a(C?`terminated by signal ${C}`:`exited with code ${u}`)}),setTimeout(()=>{l("still running after 3s, assuming success")},3e3).unref(),p.unref()}generateState(){return We.randomBytes(32).toString("hex")}buildAuthUrl(e){let t=f(),n=new URLSearchParams({client_id:t.aws.cognitoClientId,response_type:"code",scope:"email openid profile",redirect_uri:Se,state:e});return`https://${t.aws.cognitoDomain}/oauth2/authorize?${n.toString()}`}async exchangeCodeForTokens(e){let t=f(),n=`https://${t.aws.cognitoDomain}/oauth2/token`,s=new URLSearchParams({grant_type:"authorization_code",client_id:t.aws.cognitoClientId,code:e,redirect_uri:Se}),i=await B(n,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:s.toString()},"Token exchange");if(!i.ok){let a=await i.text();throw new Error(`Token exchange failed: ${i.status} ${a}`)}let o=await i.json();return{accessToken:o.access_token,idToken:o.id_token,refreshToken:o.refresh_token,expiresIn:o.expires_in}}decodeJwt(e){let t=e.split(".");if(t.length!==3)throw new Error("Invalid JWT");return JSON.parse(Buffer.from(t[1],"base64").toString("utf-8"))}async refreshTokens(e){let t=f(),n=`https://${t.aws.cognitoDomain}/oauth2/token`,s=new URLSearchParams({grant_type:"refresh_token",client_id:t.aws.cognitoClientId,refresh_token:e}),i=await B(n,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:s.toString()},"Token refresh");if(!i.ok)throw new Error(`Token refresh failed: ${i.status}`);let o=await i.json();return{accessToken:o.access_token,idToken:o.id_token,expiresIn:o.expires_in}}async login(){let e=await g.getTokens(h());if(e&&!g.isTokenExpired(e))return e;let t=this.generateState(),n=this.buildAuthUrl(t);return new Promise((s,i)=>{let o=be.createServer(async(a,l)=>{if(!a.url?.startsWith(ze)){l.writeHead(404),l.end("Not found");return}try{let p=new URL(a.url,`http://localhost:${z}`),m=p.searchParams.get("code"),u=p.searchParams.get("state"),C=p.searchParams.get("error");if(C)throw new Error(`OAuth error: ${C}`);if(u!==t)throw new Error("State mismatch");if(!m)throw new Error("No authorization code");let S=await this.exchangeCodeForTokens(m),H=this.decodeJwt(S.idToken),w={accessToken:S.accessToken,idToken:S.idToken,refreshToken:S.refreshToken,expiresAt:Date.now()+S.expiresIn*1e3,userId:H.sub,email:H.email||"unknown"};await g.setTokens(w,h()),l.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),l.end(`
|
|
169
|
+
`};var qe=(a=>(a.USER_PROMPT="USER_PROMPT",a.ASSISTANT_RESPONSE="ASSISTANT_RESPONSE",a.TOOL_USE="TOOL_USE",a.NOTIFICATION="NOTIFICATION",a.INTERACTIVE_PROMPT="INTERACTIVE_PROMPT",a.PROMPT_RESPONSE="PROMPT_RESPONSE",a.REASONING="REASONING",a))(qe||{}),ye=(t=>(t.DESKTOP="DESKTOP",t.MOBILE="MOBILE",t))(ye||{}),He=(n=>(n.SENT="SENT",n.DELIVERED="DELIVERED",n.EXECUTED="EXECUTED",n))(He||{});var fe=(n=>(n.ACTIVE="ACTIVE",n.INACTIVE="INACTIVE",n.PAUSED="PAUSED",n))(fe||{}),Ve=(n=>(n.CLAUDE="CLAUDE",n.GEMINI="GEMINI",n.CODEX="CODEX",n))(Ve||{});var F={urgentMaxAttempts:10,baseDelayMs:1e3,maxDelayMs:6e4,backoffMultiplier:2,persistentDelayMs:300*1e3},J=class{constructor(){this.authenticated=!1;this.currentUserId=null;this.currentEmail=null;this.tokens=null;this.activeSubscriptions=new Map;this.heartbeatTimers=new Map;this.environment=h(),c.info("[AppSyncClient] Initialized",{environment:this.environment})}getCurrentUserId(){if(!this.currentUserId)throw new Error("Not authenticated. Call authenticateWithStoredTokens() first.");return this.currentUserId}getCurrentUserEmail(){return this.currentEmail}async authenticateWithStoredTokens(){try{let e=await g.getTokens(this.environment);if(!e)return c.debug("[AppSyncClient] No stored tokens found"),!1;if(c.info("[AppSyncClient] Found stored OAuth tokens",{userId:e.userId,email:e.email,expired:g.isTokenExpired(e)}),g.isTokenExpired(e)){if(c.info("[AppSyncClient] Tokens expired, attempting refresh..."),!await this.refreshTokens(e))return c.warn("[AppSyncClient] Token refresh failed"),!1}else this.tokens=e;return this.currentUserId=this.tokens.userId,this.currentEmail=this.tokens.email,this.authenticated=!0,c.info("[AppSyncClient] Authenticated successfully",{userId:this.currentUserId,email:this.currentEmail}),!0}catch(e){return c.error("[AppSyncClient] Authentication failed:",e),!1}}async refreshTokens(e){try{let t=f(),n=`https://${t.aws.cognitoDomain}/oauth2/token`,i=new URLSearchParams({grant_type:"refresh_token",client_id:t.aws.cognitoClientId,refresh_token:e.refreshToken}),s=await B(n,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:i.toString()},"Token refresh");if(!s.ok)return c.error("[AppSyncClient] Token refresh failed",{status:s.status}),!1;let o=await s.json(),a={...e,accessToken:o.access_token,idToken:o.id_token,expiresAt:Date.now()+o.expires_in*1e3};return await g.setTokens(a,this.environment),this.tokens=a,c.info("[AppSyncClient] Tokens refreshed",{expiresAt:new Date(a.expiresAt).toISOString()}),!0}catch(t){return c.error("[AppSyncClient] Token refresh error:",t),!1}}isAuthenticated(){return this.authenticated}signOut(){this.authenticated=!1,this.tokens=null,this.currentUserId=null,this.currentEmail=null,this.cleanupSubscriptions(),c.info("[AppSyncClient] Signed out")}async graphqlRequest(e,t,n=!1){let i=f();if(!this.tokens?.idToken)throw new Error('Not authenticated. Run "codevibe login" first.');let s={"Content-Type":"application/json",Authorization:this.tokens.idToken},o=await B(i.aws.appsyncUrl,{method:"POST",headers:s,body:JSON.stringify({query:e,variables:t})},"AppSync GraphQL request"),a=await o.json();if(o.status===401&&!n&&this.tokens){if(c.info("[AppSyncClient] 401 Unauthorized, refreshing token..."),await this.refreshTokens(this.tokens))return this.graphqlRequest(e,t,!0);throw new Error("Token expired and refresh failed")}if(!o.ok)throw new Error(`GraphQL request failed: ${o.status}`);if(a.errors?.length)throw new Error(`GraphQL error: ${a.errors[0].message}`);return a}async createSession(e){let t={...e,metadata:e.metadata?JSON.stringify(e.metadata):void 0},n=await this.graphqlRequest(k.createSession,{input:t});return c.info("[AppSyncClient] Session created",{sessionId:n.data.createSession.sessionId}),n.data.createSession}async updateSession(e){let t={...e,metadata:e.metadata?JSON.stringify(e.metadata):void 0},n=await this.graphqlRequest(k.updateSession,{input:t});return c.debug("[AppSyncClient] Session updated",{sessionId:n.data.updateSession.sessionId}),n.data.updateSession}async getSession(e){return(await this.graphqlRequest(R.getSession,{sessionId:e})).data.getSession}async createEvent(e){let t={...e,metadata:e.metadata?JSON.stringify(e.metadata):void 0},n=await this.graphqlRequest(k.createEvent,{input:t});return c.debug("[AppSyncClient] Event created",{eventId:n.data.createEvent.eventId,type:n.data.createEvent.type}),n.data.createEvent}async updateEventStatus(e){return(await this.graphqlRequest(k.updateEventStatus,{input:e})).data.updateEventStatus}async listEvents(e,t,n){return(await this.graphqlRequest(R.listEvents,{sessionId:e,source:t,limit:n})).data.listEvents.items}async listUserDeviceKeys(){return(await this.graphqlRequest(R.listUserDeviceKeys,{})).data.listUserDeviceKeys||[]}async registerDeviceKey(e,t,n,i){let s={deviceId:e,publicKey:t,platform:n,deviceName:i};await this.graphqlRequest(k.registerDeviceKey,{input:s}),c.info("[AppSyncClient] Device key registered",{deviceId:e,platform:n})}async getAttachmentDownloadUrl(e){return(await this.graphqlRequest(k.getAttachmentDownloadUrl,{s3Key:e})).data.getAttachmentDownloadUrl}subscribeToEvents(e,t,n){c.info("[AppSyncClient] Subscribing to events",{sessionId:e});let i=this.activeSubscriptions.get(e);i&&(this.cleanupSubscriptionState(i),this.activeSubscriptions.delete(e));let s={ws:null,subscriptionId:(0,ve.v4)(),sessionId:e,onEvent:t,onError:n,reconnectAttempts:0,isReconnecting:!1,destroyed:!1};return this.activeSubscriptions.set(e,s),this.createSubscription(s),()=>{this.cleanupSubscriptionState(s),this.activeSubscriptions.delete(e)}}buildRealtimeUrl(){let e=f(),t=e.aws.appsyncUrl.replace("https://","wss://").replace("appsync-api","appsync-realtime-api"),n={host:new URL(e.aws.appsyncUrl).host};this.tokens?.idToken&&(n.Authorization=this.tokens.idToken);let i=Buffer.from(JSON.stringify(n)).toString("base64"),s=Buffer.from(JSON.stringify({})).toString("base64");return`${t}?header=${i}&payload=${s}`}createSubscription(e){let{sessionId:t,subscriptionId:n,onEvent:i,onError:s}=e;try{let o=this.buildRealtimeUrl(),a=new he.default(o,["graphql-ws"]);a.on("open",()=>{c.info("[AppSyncClient] WebSocket connected",{sessionId:t}),a.send(JSON.stringify({type:"connection_init"}))}),a.on("message",l=>{try{let p=JSON.parse(l.toString());switch(p.type){case"connection_ack":this.sendSubscriptionStart(a,e);break;case"start_ack":c.info("[AppSyncClient] Subscription started",{sessionId:t}),e.isReconnecting=!1,e.reconnectAttempts=0,this.startHeartbeat(t);break;case"data":this.resetKeepAliveTimer(e);let m=p.payload?.data?.onEventCreated;m&&m.source==="MOBILE"&&i(m);break;case"ka":this.resetKeepAliveTimer(e);break;case"error":let u=p.payload?.errors?.[0]?.message||"Unknown error";this.handleSubscriptionError(e,new Error(u));break}}catch(p){c.error("[AppSyncClient] Failed to parse message",{error:p})}}),a.on("error",l=>{c.error("[AppSyncClient] WebSocket error",{sessionId:t,error:l.message}),this.handleSubscriptionError(e,l)}),a.on("close",(l,p)=>{c.info("[AppSyncClient] WebSocket closed",{sessionId:t,code:l}),e.keepAliveTimer&&clearTimeout(e.keepAliveTimer),!e.destroyed&&this.activeSubscriptions.get(t)===e&&this.handleSubscriptionError(e,new Error(`WebSocket closed: ${l}`))}),e.ws=a,this.resetKeepAliveTimer(e)}catch(o){this.handleSubscriptionError(e,o)}}sendSubscriptionStart(e,t){let n=f(),{sessionId:i,subscriptionId:s}=t,o={host:new URL(n.aws.appsyncUrl).host};this.tokens?.idToken&&(o.Authorization=this.tokens.idToken),e.send(JSON.stringify({id:s,type:"start",payload:{data:JSON.stringify({query:j.onEventCreated,variables:{sessionId:i}}),extensions:{authorization:o}}}))}resetKeepAliveTimer(e){e.keepAliveTimer&&clearTimeout(e.keepAliveTimer),e.keepAliveTimer=setTimeout(()=>{this.handleSubscriptionError(e,new Error("Keep-alive timeout"))},300*1e3)}handleSubscriptionError(e,t){let{sessionId:n,onError:i}=e;if(e.isReconnecting||!this.activeSubscriptions.has(n))return;e.isReconnecting=!0,e.reconnectAttempts++,this.stopHeartbeat(n);let s=e.reconnectAttempts<=F.urgentMaxAttempts,o;if(s?o=Math.min(F.baseDelayMs*Math.pow(F.backoffMultiplier,e.reconnectAttempts-1),F.maxDelayMs):(o=F.persistentDelayMs,e.reconnectAttempts===F.urgentMaxAttempts+1&&c.info("[AppSyncClient] Switching to persistent reconnect (every 5min)",{sessionId:n})),c.info("[AppSyncClient] Scheduling reconnect",{sessionId:n,attempt:e.reconnectAttempts,phase:s?"urgent":"persistent",delayMs:o}),e.ws){try{e.ws.close(1e3)}catch{}e.ws=null}e.keepAliveTimer&&clearTimeout(e.keepAliveTimer),e.reconnectTimer=setTimeout(async()=>{if(e.isReconnecting=!1,e.destroyed||this.activeSubscriptions.get(n)!==e){c.info("[AppSyncClient] Reconnect skipped \u2014 state is no longer canonical",{sessionId:n});return}try{let a=await g.getTokens(this.environment);a&&(g.isTokenExpired(a)?await this.refreshTokens(a)&&c.info("[AppSyncClient] Tokens refreshed before reconnect",{sessionId:n}):this.tokens=a)}catch{c.warn("[AppSyncClient] Token refresh failed before reconnect, using existing tokens",{sessionId:n})}if(e.destroyed||this.activeSubscriptions.get(n)!==e){c.info("[AppSyncClient] Reconnect skipped after token refresh \u2014 state no longer canonical",{sessionId:n});return}e.subscriptionId=(0,ve.v4)(),this.createSubscription(e)},o)}cleanupSubscriptionState(e){if(e.destroyed=!0,e.reconnectTimer&&(clearTimeout(e.reconnectTimer),e.reconnectTimer=void 0),e.keepAliveTimer&&(clearTimeout(e.keepAliveTimer),e.keepAliveTimer=void 0),e.ws){try{e.ws.readyState===he.default.OPEN&&e.ws.send(JSON.stringify({id:e.subscriptionId,type:"stop"}))}catch{}try{e.ws.close(1e3)}catch{}try{e.ws.removeAllListeners()}catch{}e.ws=null}}startHeartbeat(e,t=120*1e3){this.stopHeartbeat(e),this.sendHeartbeat(e);let n=setInterval(()=>{this.sendHeartbeat(e)},t);this.heartbeatTimers.set(e,n),c.info("[AppSyncClient] Heartbeat started",{sessionId:e,intervalMs:t})}stopHeartbeat(e){let t=this.heartbeatTimers.get(e);t&&(clearInterval(t),this.heartbeatTimers.delete(e),c.info("[AppSyncClient] Heartbeat stopped",{sessionId:e}))}async sendHeartbeat(e){try{await this.updateSession({sessionId:e,lastHeartbeatAt:new Date().toISOString()}),c.debug("[AppSyncClient] Heartbeat sent",{sessionId:e})}catch(t){c.warn("[AppSyncClient] Heartbeat failed",{sessionId:e,error:t})}}cleanupSubscriptions(){this.activeSubscriptions.forEach(e=>{this.cleanupSubscriptionState(e)}),this.activeSubscriptions.clear(),this.heartbeatTimers.forEach(e=>clearInterval(e)),this.heartbeatTimers.clear()}};var We=y(require("crypto")),je=y(require("fs")),be=y(require("http")),Je=require("child_process");_();M();L();var z=8080,ze="/callback",Se=`http://localhost:${z}${ze}`,q=class r{constructor(){}static getInstance(){return r.instance||(r.instance=new r),r.instance}openBrowser(e){console.log(""),console.log("Opening your browser for sign-in..."),console.log("If your browser does not open automatically, visit this URL manually:"),console.log(` ${e}`),console.log("");let t=this.getBrowserCommands();this.tryBrowserCommand(t,e,0)}getBrowserCommands(){let e=process.platform;if(e==="darwin")return[{cmd:"open",fixedArgs:[]}];if(e==="win32")return[{cmd:"cmd",fixedArgs:["/c","start",""]}];let t=[];return this.isRunningInWSL()&&(t.push({cmd:"wslview",fixedArgs:[]}),t.push({cmd:"cmd.exe",fixedArgs:["/c","start",""]}),t.push({cmd:"powershell.exe",fixedArgs:["-NoProfile","-Command","Start-Process"]})),t.push({cmd:"xdg-open",fixedArgs:[]}),t}isRunningInWSL(){if(process.platform!=="linux")return!1;try{let e=je.readFileSync("/proc/sys/kernel/osrelease","utf8");return/microsoft|wsl/i.test(e)}catch{return!1}}tryBrowserCommand(e,t,n){if(n>=e.length){c.debug("[AuthService] No browser-opening command succeeded. User must open the sign-in URL manually (printed to stdout above).");return}let i=e[n],s=[...i.fixedArgs,t],o=!1,a=u=>{o||(o=!0,c.debug(`[AuthService] Browser command '${i.cmd}' ${u}; trying next fallback`),this.tryBrowserCommand(e,t,n+1))},l=u=>{o||(o=!0,c.debug(`[AuthService] Browser command '${i.cmd}' ${u}`))},p;try{p=(0,Je.spawn)(i.cmd,s,{detached:!0,stdio:"ignore"})}catch(u){a(`threw synchronously: ${u?.message||u}`);return}p.on("error",u=>{a(`failed to spawn: ${u?.message||u}`)}),p.on("exit",(u,C)=>{u===0?l("exited successfully"):a(C?`terminated by signal ${C}`:`exited with code ${u}`)}),setTimeout(()=>{l("still running after 3s, assuming success")},3e3).unref(),p.unref()}generateState(){return We.randomBytes(32).toString("hex")}buildAuthUrl(e){let t=f(),n=new URLSearchParams({client_id:t.aws.cognitoClientId,response_type:"code",scope:"email openid profile",redirect_uri:Se,state:e});return`https://${t.aws.cognitoDomain}/oauth2/authorize?${n.toString()}`}async exchangeCodeForTokens(e){let t=f(),n=`https://${t.aws.cognitoDomain}/oauth2/token`,i=new URLSearchParams({grant_type:"authorization_code",client_id:t.aws.cognitoClientId,code:e,redirect_uri:Se}),s=await B(n,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:i.toString()},"Token exchange");if(!s.ok){let a=await s.text();throw new Error(`Token exchange failed: ${s.status} ${a}`)}let o=await s.json();return{accessToken:o.access_token,idToken:o.id_token,refreshToken:o.refresh_token,expiresIn:o.expires_in}}decodeJwt(e){let t=e.split(".");if(t.length!==3)throw new Error("Invalid JWT");return JSON.parse(Buffer.from(t[1],"base64").toString("utf-8"))}async refreshTokens(e){let t=f(),n=`https://${t.aws.cognitoDomain}/oauth2/token`,i=new URLSearchParams({grant_type:"refresh_token",client_id:t.aws.cognitoClientId,refresh_token:e}),s=await B(n,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:i.toString()},"Token refresh");if(!s.ok)throw new Error(`Token refresh failed: ${s.status}`);let o=await s.json();return{accessToken:o.access_token,idToken:o.id_token,expiresIn:o.expires_in}}async login(){let e=await g.getTokens(h());if(e&&!g.isTokenExpired(e))return e;let t=this.generateState(),n=this.buildAuthUrl(t);return new Promise((i,s)=>{let o=be.createServer(async(a,l)=>{if(!a.url?.startsWith(ze)){l.writeHead(404),l.end("Not found");return}try{let p=new URL(a.url,`http://localhost:${z}`),m=p.searchParams.get("code"),u=p.searchParams.get("state"),C=p.searchParams.get("error");if(C)throw new Error(`OAuth error: ${C}`);if(u!==t)throw new Error("State mismatch");if(!m)throw new Error("No authorization code");let S=await this.exchangeCodeForTokens(m),H=this.decodeJwt(S.idToken),w={accessToken:S.accessToken,idToken:S.idToken,refreshToken:S.refreshToken,expiresAt:Date.now()+S.expiresIn*1e3,userId:H.sub,email:H.email||"unknown"};await g.setTokens(w,h()),l.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),l.end(`
|
|
170
170
|
<!DOCTYPE html>
|
|
171
171
|
<html>
|
|
172
172
|
<head><title>Success</title></head>
|
|
@@ -175,7 +175,7 @@ ${n.stack}`)):typeof n=="object"?o+=` ${JSON.stringify(n,it)}`:o+=` ${n}`),o}log
|
|
|
175
175
|
<p>You can close this window.</p>
|
|
176
176
|
</body>
|
|
177
177
|
</html>
|
|
178
|
-
`),setTimeout(()=>{o.close(()=>
|
|
178
|
+
`),setTimeout(()=>{o.close(()=>i(w))},500)}catch(p){let m=String(p?.message||p).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");l.writeHead(400,{"Content-Type":"text/html; charset=utf-8"}),l.end(`
|
|
179
179
|
<!DOCTYPE html>
|
|
180
180
|
<html>
|
|
181
181
|
<head><title>Error</title></head>
|
|
@@ -185,7 +185,7 @@ ${n.stack}`)):typeof n=="object"?o+=` ${JSON.stringify(n,it)}`:o+=` ${n}`),o}log
|
|
|
185
185
|
<p style="text-align: center; color: #71717a; margin-top: 24px;">You can close this window and try again in your terminal.</p>
|
|
186
186
|
</body>
|
|
187
187
|
</html>
|
|
188
|
-
`),setTimeout(()=>{o.close(()=>
|
|
188
|
+
`),setTimeout(()=>{o.close(()=>s(p))},500)}});o.on("error",a=>{a.code==="EADDRINUSE"?s(new Error(`Port ${z} is in use`)):s(a)}),o.listen(z,"localhost",()=>{c.info("[AuthService] Callback server started"),this.openBrowser(n)}),setTimeout(()=>{o.close(()=>s(new Error("Login timeout")))},120*1e3)})}async logout(){let e=f(),t=await g.deleteTokens(h());return t&&new Promise(n=>{let i=be.createServer((s,o)=>{s.url?.startsWith("/signout")?(o.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),o.end(`
|
|
189
189
|
<!DOCTYPE html>
|
|
190
190
|
<html>
|
|
191
191
|
<head><title>Signed Out</title></head>
|
|
@@ -194,7 +194,7 @@ ${n.stack}`)):typeof n=="object"?o+=` ${JSON.stringify(n,it)}`:o+=` ${n}`),o}log
|
|
|
194
194
|
<p>You can close this window.</p>
|
|
195
195
|
</body>
|
|
196
196
|
</html>
|
|
197
|
-
`),setTimeout(()=>{
|
|
197
|
+
`),setTimeout(()=>{i.close(()=>n(!0))},500)):(o.writeHead(404),o.end("Not found"))});i.on("error",()=>{n(!0)}),i.listen(z,"localhost",()=>{let s=`https://${e.aws.cognitoDomain}/logout?client_id=${e.aws.cognitoClientId}&logout_uri=${encodeURIComponent(Se.replace("/callback","/signout"))}`;this.openBrowser(s)}),setTimeout(()=>{i.close(()=>n(!0))},30*1e3)})}async getStatus(){let e=await g.getTokens(h());return e?{authenticated:!g.isTokenExpired(e),tokens:e}:{authenticated:!1}}},x=q.getInstance();_();var d={reset:"\x1B[0m",green:"\x1B[32m",red:"\x1B[31m",yellow:"\x1B[33m",cyan:"\x1B[36m",dim:"\x1B[2m"};async function yt(){console.log(`${d.cyan}CodeVibe Login${d.reset}
|
|
198
198
|
`);try{let r=await x.getStatus();if(r.authenticated&&r.tokens){console.log(`${d.yellow}Already logged in as: ${r.tokens.email}${d.reset}`),console.log(`Token expires: ${new Date(r.tokens.expiresAt).toLocaleString()}`),console.log(`
|
|
199
199
|
Run '${d.dim}codevibe logout${d.reset}' to sign out first.`),process.exit(0);return}console.log("Opening browser for authentication..."),console.log(`${d.dim}Waiting for callback...${d.reset}
|
|
200
200
|
`);let e=await x.login();e&&(console.log(`
|
|
@@ -209,12 +209,12 @@ ${d.dim}Token will be refreshed automatically.${d.reset}`),process.exit(0)}catch
|
|
|
209
209
|
`);let{keychainManager:r}=await Promise.resolve().then(()=>(M(),Me));try{await r.clearAllData(),console.log(`${d.green}\u2713 Device reset complete.${d.reset}`),console.log(` Run '${d.dim}codevibe login${d.reset}' to set up again.`),process.exit(0)}catch(e){console.log(`${d.red}\u2717 Reset failed: ${e.message}${d.reset}`),process.exit(1)}}function St(){console.log(`CodeVibe Authentication
|
|
210
210
|
`),console.log("Usage:"),console.log(" codevibe login - Sign in via browser"),console.log(" codevibe logout - Sign out"),console.log(" codevibe status - Show auth status"),console.log(" codevibe reset-device - Reset device identity (destructive)"),console.log(`
|
|
211
211
|
Environment:`),console.log(' Set ENVIRONMENT env var to "development" or "production" (default)'),console.log(" Example: ENVIRONMENT=development codevibe login")}async function ne(r){let e=h();console.log(`${d.dim}Environment: ${e}${d.reset}
|
|
212
|
-
`);let n=r.slice(2).filter(
|
|
212
|
+
`);let n=r.slice(2).filter(i=>!i.startsWith("--"))[0];switch(n){case"login":await yt();break;case"logout":await ft();break;case"status":await ht();break;case"reset-device":await vt();break;default:St(),process.exit(n?1:0)}}require.main===module&&ne(process.argv).catch(r=>{console.error("Error:",r),process.exit(1)});_();L();var bt=/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g;function Ye(r){let e=Ee(r);if(!e)return null;let t=wt(e);if(t)return t;let n=kt(e);return n||null}function Ee(r){return r.replace(/\r/g,`
|
|
213
213
|
`).replace(bt,"").replace(/[│┌┐└┘─├┤┬┴┼╌╎╭╮╯╰║═╔╗╚╝╠╣╦╩╬]/g," ").replace(/[ \t]+\n/g,`
|
|
214
214
|
`).replace(/\n{3,}/g,`
|
|
215
215
|
|
|
216
216
|
`).trim()}function wt(r){let e=r.split(`
|
|
217
|
-
`).map(m=>m.trim()),t=Et(e,m=>/\[(?:y\/n|Y\/n|y\/N)\]/.test(m)),n=t>=0?e[t]:null;if(!n)return null;let
|
|
218
|
-
`):n,o=
|
|
219
|
-
`).map(l=>l.trim()),t=It(e);if(t.length<2)return null;let n=t.map(({line:l})=>Ge(l)).filter(l=>!!l),
|
|
220
|
-
`):"Select an option",options:n,submitMap:
|
|
217
|
+
`).map(m=>m.trim()),t=Et(e,m=>/\[(?:y\/n|Y\/n|y\/N)\]/.test(m)),n=t>=0?e[t]:null;if(!n)return null;let i=Xe(e,t),s=i.length>0?i.join(`
|
|
218
|
+
`):n,o=s.toLowerCase(),a=o.includes("what to change")||o.includes("what should")||o.includes("provide")||o.includes("instructions");return{kind:"yes_no",promptText:s,options:a?[{number:"1",text:"Yes"},{number:"2",text:"No, provide instructions"}]:[{number:"1",text:"Yes"},{number:"2",text:"No"}],submitMap:{1:"y",2:"n"},requiresFollowUpText:a}}function kt(r){let e=r.split(`
|
|
219
|
+
`).map(l=>l.trim()),t=It(e);if(t.length<2)return null;let n=t.map(({line:l})=>Ge(l)).filter(l=>!!l),i={};for(let l of n)i[l.number]=l.number;let s=t[0]?.index??-1,o=Xe(e,s-1);return{kind:"numbered",promptText:o.length>0?o.join(`
|
|
220
|
+
`):"Select an option",options:n,submitMap:i}}function Et(r,e){for(let t=r.length-1;t>=0;t-=1)if(e(r[t]))return t;return-1}function Ge(r){let e=r.match(/^(?:[>›❯▸▶➜➤*●]\s*)?(\d+)\.\s+(.*)$/);return e?{number:e[1],text:e[2]}:null}function It(r){let e=r.map((n,i)=>({index:i,line:n,parsed:Ge(n)})).filter(n=>!!n.parsed);if(e.length===0)return[];let t=[e[e.length-1]];for(let n=e.length-2;n>=0;n-=1){let i=e[n],s=t[0];if(i.index!==s.index-1)break;t.unshift(i)}return t.map(({index:n,line:i})=>({index:n,line:i}))}function Xe(r,e){if(e<0)return[];let t=we(r,e);if(t<0)return[];let{start:n,end:i}=ke(r,t),s=r.slice(n,i+1).filter(Boolean);if(xt(s)){let u=Tt(r,n-1);return u.length>0?u:s}if(n<=1)return s;let o=n-1;if(o=we(r,o),o<0||o===n-1)return s;let{start:a,end:l}=ke(r,o),p=r.slice(a,l+1).filter(Boolean);return p.some(Qe)?[...p,...s]:s}function Qe(r){return/^(?:would you like to|do you want to|the model would like to|action required|confirm)\b/i.test(r)}function we(r,e){let t=e;for(;t>=0&&!r[t];)t-=1;return t}function ke(r,e){let t=e;for(;t>=0&&r[t];)t-=1;return{start:t+1,end:e}}function Tt(r,e){let t=[],n=e;for(;n>=0&&t.length<2&&(n=we(r,n),!(n<0));){let{start:s,end:o}=ke(r,n),a=r.slice(s,o+1).filter(Boolean);a.length>0&&t.unshift(a),n=s-1}if(t.length===0)return[];let i=t.findIndex(s=>s.some(Qe));return i>=0?t.slice(i).flat():t[t.length-1]}function xt(r){return r.length===0?!1:r.filter(At).length>=Math.max(2,Math.ceil(r.length/2))}function At(r){return/^\d+\s/.test(r)}Q();M();async function re(r,e,t){try{let n=await e.listUserDeviceKeys();if(n.length===0)return t.info("No device keys found, session will not be encrypted"),null;t.info("Preparing session encryption",{sessionId:r,deviceCount:n.length});let{sessionKey:i,encryptedKeys:s}=g.createSessionKey(n);return t.info("Session encryption prepared",{sessionId:r,deviceCount:s.length}),{sessionKey:i,encryptedKeys:s}}catch(n){return t.warn("Failed to prepare session encryption:",n),null}}async function Ie(r,e,t){let{sessionId:n,userId:i,agentType:s,projectPath:o,metadata:a}=r,l=null;try{l=await e.getSession(n)}catch(S){t.warn("Failed to get session (will attempt to create new)",{sessionId:n,error:S})}if(l){t.info("Session exists in backend - reactivating",{sessionId:n,previousStatus:l.status});try{await e.updateSession({sessionId:n,status:"ACTIVE"})}catch(w){t.warn("Failed to reactivate existing session, will continue",{sessionId:n,error:w})}let S=null,H=l.encryptedKeys;if(l.isEncrypted&&H?.length)try{let w=await g.getSessionKey(n,H);w?(S=w,g.cacheSessionKey(n,w),t.info("Session key retrieved for resumed session",{sessionId:n})):t.warn("No encrypted key for this device; proceeding without decryption",{sessionId:n})}catch(w){t.warn("Failed to retrieve session key for resumed session",{sessionId:n,error:w})}return{resumed:!0,sessionKey:S}}let p=await re(n,e,t),m=o,u=a;p&&(m=b.encryptContent(o,p.sessionKey),u&&Object.keys(u).length>0&&(u={encrypted:b.encryptMetadata(u,p.sessionKey)}),t.info("Session data encrypted",{sessionId:n})),t.info("Creating new session in backend",{sessionId:n,userId:i,agentType:s,isEncrypted:!!p}),await e.createSession({sessionId:n,userId:i,agentType:s,projectPath:m,status:"ACTIVE",metadata:u,isEncrypted:p?!0:void 0,creatorDeviceId:p?await g.getDeviceId():void 0,encryptionVersion:p?1:void 0,encryptedKeys:p?.encryptedKeys});let C=p?.sessionKey||null;return p&&g.cacheSessionKey(n,p.sessionKey),t.info("Session created",{sessionId:n,userId:i,isEncrypted:!!p}),{resumed:!1,sessionKey:C}}0&&(module.exports={AgentType,AppSyncClient,AuthService,CryptoError,CryptoService,DeliveryStatus,ENCRYPTION_VERSION,EventSource,EventType,KeychainError,KeychainManager,Logger,SessionStatus,authService,createLogger,cryptoService,getConfig,getEnvironment,keychainManager,loadConfig,logger,mutations,normalizeSnapshot,parseInteractivePrompt,prepareSessionEncryption,queries,resumeOrCreateSession,runAuthCli,subscriptions});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quantiya/codevibe-claude-plugin",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.17",
|
|
4
4
|
"description": "Control Claude Code from your iPhone and Android — real-time sync, approve file edits, send prompts by voice. Part of CodeVibe.",
|
|
5
5
|
"main": "dist/server.js",
|
|
6
6
|
"bin": {
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"node": ">=18.0.0"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@quantiya/codevibe-core": "^1.0.
|
|
50
|
+
"@quantiya/codevibe-core": "^1.0.9",
|
|
51
51
|
"dotenv": "^16.6.1",
|
|
52
52
|
"express": "^5.1.0",
|
|
53
53
|
"graphql": "^16.12.0",
|