@shardflux/desktop-linux-arm64 0.1.0-next.5 → 0.1.1
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/CHANGELOG.md +10 -3
- package/app/manifest.json +1 -1
- package/app/shardflux-desktop +0 -0
- package/app/ui/app.mjs +5 -3
- package/app/ui/core.mjs +35 -0
- package/app/ui/surfaces.mjs +2 -0
- package/package.json +4 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.1.
|
|
3
|
+
## 0.1.1 (not yet published)
|
|
4
4
|
|
|
5
|
-
-
|
|
5
|
+
- The app reads `shard`'s answers whole: Settings → Agents shows Connected (or Connect) instead of staying on
|
|
6
|
+
Checking…, the top-bar panel refreshes your conversations, and a conversation you start opens instead of showing
|
|
7
|
+
its details as an error. A note shard prints next to its answer (such as `Initialized a git repository …`) no
|
|
8
|
+
longer gets in the way, and a failed command shows its message. Installed with CLI 0.18.1+.
|
|
6
9
|
|
|
7
|
-
|
|
10
|
+
## 0.1.0 (2026-10-10)
|
|
11
|
+
|
|
12
|
+
- The Shardflux app for Ubuntu 24.04 (arm64): guided sign-in and agent setup, the top-bar panel for your
|
|
13
|
+
conversations, the launcher (Alt+Shift+Space), Settings, device and sign-in approvals, notifications with actions,
|
|
14
|
+
file downloads and self-update. Installed with `npm i -g shardflux` (CLI 0.18.0+) and opened by `shard`.
|
package/app/manifest.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"id":"dev.shardflux.Desktop","version":"0.1.
|
|
1
|
+
{"id": "dev.shardflux.Desktop", "version": "0.1.1"}
|
package/app/shardflux-desktop
CHANGED
|
Binary file
|
package/app/ui/app.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {icon,appMark,spinner} from './icons.mjs';
|
|
2
2
|
import {installTray} from './tray.mjs';
|
|
3
|
-
import {steps,samplePrompt,readProgress,canContinue,anyAgentConnected,accountConnected,demoPhase,cardState,commands,recordStart,DemoTurn,browserEvent,streamURL,safeHTTPS,observeActivity,recap,openPlace,shortcutLabel,escape as e} from './core.mjs';
|
|
3
|
+
import {steps,samplePrompt,readProgress,cliResult,streams,canContinue,anyAgentConnected,accountConnected,demoPhase,cardState,commands,recordStart,DemoTurn,browserEvent,streamURL,safeHTTPS,observeActivity,recap,openPlace,shortcutLabel,escape as e} from './core.mjs';
|
|
4
4
|
import {agentRows} from './agents.mjs';
|
|
5
5
|
import {installSurfaces} from './surfaces.mjs';
|
|
6
6
|
import {installSettings} from './settings.mjs';
|
|
@@ -14,7 +14,9 @@ model.rows=[];
|
|
|
14
14
|
model.api=api;
|
|
15
15
|
window.addEventListener('sf-profile',async ev=>{api=ev.detail;model.api=api;model.p=readProgress(await host('read',{name:'onboarding.json'}),api);model.error='';await refresh();render();});
|
|
16
16
|
export const button=(title,action,cls='',disabled=false)=>`<button class="${cls}" data-action="${action}" ${disabled?'disabled':''}>${title}</button>`;
|
|
17
|
-
|
|
17
|
+
// stdout and stderr arrive apart (see cliResult): the result is stdout's JSON document, or its NDJSON events for the
|
|
18
|
+
// browser sign-in. A run that could not start at all rejects in the native host.
|
|
19
|
+
export async function cli(argv,options={}) {return cliResult(await host('cli',{argv,...options}),{stream:streams(argv)});}
|
|
18
20
|
export async function openConversation(key,target='auto') {const d=await cli(commands.open(key,target));if(d.url)await host('open',{url:d.url});else if(d.target==='terminal')await host('terminal',{argv:d.command||['shard','agents','open',key]});else if(!d.opened&&!d.ok)throw new Error('shard did not return a conversation destination. Update Shardflux and try Open again.');return d;}
|
|
19
21
|
export const save=()=>host('write',{name:'onboarding.json',value:model.p});
|
|
20
22
|
export const savePrefs=()=>host('write',{name:'desktop-linux.json',value:model.prefs});
|
|
@@ -52,7 +54,7 @@ export function render(){
|
|
|
52
54
|
}
|
|
53
55
|
async function signedIn(email){model.email=email;model.signIn='done';model.p.completed=[...new Set([...model.p.completed,0])];render();await cli(commands.setup);await refresh();model.signIn='idle';await go(1);}
|
|
54
56
|
let browserRun;
|
|
55
|
-
async function signIn(browser){model.error='';model.signIn=browser?'browser':'working';render();if(browser){
|
|
57
|
+
async function signIn(browser){model.error='';model.signIn=browser?'browser':'working';render();if(browser){const argv=commands.browser('Shardflux for Linux');browserRun=host('cli',{argv,stream:true,timeout:540});const run=await browserRun;if(model.signIn==='browser'&&run.status!==0)cliResult(run,{stream:true});return;}try{const doc=await cli(commands.signIn);await signedIn(doc.user?.email);}catch(err){if(/mfa|two.factor|verification/i.test(err.message)){model.signIn='mfa';render();}else throw err;}}
|
|
56
58
|
window.addEventListener('sf-cli-line',async ev=>{if(ev.detail.id!==browserRun?.id)return;const value=browserEvent(ev.detail.line);if(value?.event==='open'){model.code=value.user_code;model.url=value.verification_uri_complete;render();}else if(value?.event==='signed_in'){try{await signedIn(value.email);}catch(err){model.error=err.message;render();}}else if(value?.event==='error'){model.error=value.message;model.signIn='idle';render();}});
|
|
57
59
|
async function go(step){model.p.step=step;await save();if(step===2&&!model.p.sample_path){const d=await cli(commands.sample);if(!d.sample_path?.startsWith('/'))throw new Error('The sample project could not be prepared.');model.p.sample_path=d.sample_path;await save();}render();}
|
|
58
60
|
export async function connect(id){const c=model.inspection?.connections.find(c=>c.id===id);if(c?.action?.url){await host('open',{url:safeHTTPS(c.action.url)});return;}if(c?.action?.retry){await refresh(true,id==='codex');return;}if(c?.action?.command){const argv=c.action.command;if(argv[0]==='shard'){await cli([...argv.slice(1).filter(a=>a!=='--json'),'--json']);}else await host('terminal',{argv});}else await refresh(true,id==='codex');await refresh();}
|
package/app/ui/core.mjs
CHANGED
|
@@ -55,6 +55,41 @@ export function reference(doc,joined=false) {
|
|
|
55
55
|
if(doc.workspace?.workspace_key)return doc.workspace.workspace_key;
|
|
56
56
|
throw new Error('shard did not return a conversation destination. Update Shardflux and try Open again.');
|
|
57
57
|
}
|
|
58
|
+
// How the page reads a shard run, as the Mac's ConversationProcess does. The native host hands over {status,
|
|
59
|
+
// timed_out, stdout, stderr} with the two streams apart. `--json` prints one JSON document on stdout, pretty-printed
|
|
60
|
+
// over many lines; the only command that streams NDJSON events (one per line) is the browser sign-in, as in
|
|
61
|
+
// MenuBarCore (BrowserSignIn.run is ConversationProcess's one onLine caller). A failure's error envelope
|
|
62
|
+
// ({"error":{code,message,...}}) is read from stdout, else from the end of stderr, where CLI 0.18.0 writes it; any
|
|
63
|
+
// other stderr text is a diagnostic ("Initialized a git repository in …") and never decides the result.
|
|
64
|
+
export const streams = argv => argv[0]==='auth' && argv[1]==='login' && argv.includes('--browser');
|
|
65
|
+
const parseJSON = text => {try{return JSON.parse(text);}catch{return undefined;}};
|
|
66
|
+
/** The whole text as one JSON document, else the last one in it: pretty-printed JSON opens at column 0 and indents
|
|
67
|
+
* everything inside, so the last line starting with { or [ starts the last document. */
|
|
68
|
+
export function lastDocument(text='') {
|
|
69
|
+
const whole=parseJSON(text.trim());if(whole!==undefined)return whole;
|
|
70
|
+
const lines=text.split('\n');
|
|
71
|
+
for(let i=lines.length-1;i>=0;i--)if(/^[{[]/.test(lines[i])){const doc=parseJSON(lines.slice(i).join('\n').trim());if(doc!==undefined)return doc;}
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
const envelope = doc => doc && typeof doc==='object' && doc.error && typeof doc.error==='object' && typeof doc.error.message==='string' ? doc.error : null;
|
|
75
|
+
/** The command's answer, or an Error carrying `detail` (the error envelope) and `doc` (what stdout said). A run that
|
|
76
|
+
* exited 0 with a readable document is a success, whatever stderr holds. */
|
|
77
|
+
export function cliResult(run, {stream=false}={}) {
|
|
78
|
+
const {status=-1,stdout='',stderr='',timed_out:timedOut=false}=run&&typeof run==='object'?run:{};
|
|
79
|
+
let doc,detail=null;
|
|
80
|
+
if(stream){const events=stdout.split('\n').map(line=>parseJSON(line.trim())).filter(v=>v&&typeof v==='object');doc=events.at(-1);const failed=events.findLast(v=>v.event==='error');if(failed)detail={code:failed.code,message:failed.message};}
|
|
81
|
+
else doc=lastDocument(stdout);
|
|
82
|
+
detail??=envelope(doc);
|
|
83
|
+
if(status===0&&doc!==undefined&&!detail)return doc;
|
|
84
|
+
if(!detail&&status!==0)detail=envelope(lastDocument(stderr));
|
|
85
|
+
const extra=doc!==undefined&&!envelope(doc)?{doc}:{};
|
|
86
|
+
if(detail)throw Object.assign(new Error(detail.message),{detail},extra);
|
|
87
|
+
if(timedOut)throw Object.assign(new Error('shard took too long. Check your conversations before starting another one.'),extra);
|
|
88
|
+
if(status===0)throw new Error('shard returned a response this app can’t read. Update Shardflux and try again.');
|
|
89
|
+
// Without an envelope only shard's own `error:` line is worth showing; a diagnostic is not the reason it failed.
|
|
90
|
+
const said=stderr.split('\n').map(l=>l.trim()).findLast(l=>l.startsWith('error: '))?.slice(7).replace(/\s*\[code=[^\]]*\]$/,'');
|
|
91
|
+
throw Object.assign(new Error(said||'shard could not finish. Check your sign-in and try again.'),extra);
|
|
92
|
+
}
|
|
58
93
|
export function browserEvent(line) {
|
|
59
94
|
try {const e=JSON.parse(line);if(e.event==='open' && e.user_code)return {...e,verification_uri_complete:safeHTTPS(e.verification_uri_complete)};if(['signed_in','error'].includes(e.event))return e;}catch{}return null;
|
|
60
95
|
}
|
package/app/ui/surfaces.mjs
CHANGED
|
@@ -175,6 +175,8 @@ export function installSurfaces({model,host,cli,save,savePrefs,render,button,reg
|
|
|
175
175
|
const failure=t3Failure(err.detail);
|
|
176
176
|
if(failure){L.remedy=t3Remedy(failure,L.targets?.t3?.mode)?failure:null;if(failure.threadID)L.created=failureReference(failure)??`t3:${failure.threadID}`;else if(failure.conversation)L.created=failure.conversation;}
|
|
177
177
|
else if(err.detail?.details?.workspace_key)L.created=err.detail.details.workspace_key;
|
|
178
|
+
// shard printed the started conversation and then failed: offer that one, never a second Start.
|
|
179
|
+
else if(err.doc){try{L.created=reference(err.doc,!!L.on);}catch{}}
|
|
178
180
|
L.error=err.message;
|
|
179
181
|
}finally{L.stage=null;if(promptFile)await host('remove-prompt',{path:promptFile});render();}
|
|
180
182
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shardflux/desktop-linux-arm64",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Shardflux desktop app for Ubuntu Linux",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"os": [
|
|
@@ -20,5 +20,8 @@
|
|
|
20
20
|
},
|
|
21
21
|
"publishConfig": {
|
|
22
22
|
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"prepack": "node scripts/check-package.mjs"
|
|
23
26
|
}
|
|
24
27
|
}
|