You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
publish: CLI app support in the submission wizard (#44)
Wire the Backend-type selector (the cli option was an inert placeholder): the
form now builds CLI submissions end-to-end. Backend step branches to a command
+ env_passthrough body; each method gets a CLI route — enumerated args (with
${param} substitution) + optional params-as-flags, or a passthrough toggle that
fronts the whole CLI (call as {"args":[...]}). Validation, the live
help/pilotctl preview, and the review table are all backend-aware. Submission
JSON carries backend.type=cli/command/env_passthrough and per-method cli routes,
matching the publish-api.
Co-authored-by: Alex Godoroja <alex@vulturelabs.io>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
<div><b>HTTP API <span class="badge ok">available</span></b><span>Your app is reachable over HTTP. We generate, sign, and verify an adapter that forwards each method to it.</span></div>
<div><b>CLI / binary <span class="badge">coming soon</span></b><span>Ship a real command-line binary to the host. Native delivery is in the works — not available yet.</span></div>
<div><b>CLI <span class="badge ok">available</span></b><span>Front a command-line tool installed on the host. Each method runs a subprocess; <code>pilotctl appstore call</code> translates into <code><cli> <args></code>.</span></div>
330
341
</label>
331
342
</div>
332
-
<div class="explain">Your adapter forwards each method to your app. Add any auth headers it needs.
333
-
A value like <code>\${WEATHER_TOKEN}</code> is a <b>secret placeholder</b>: the operator who installs the app
334
-
supplies it at install time (from their environment or a local secrets file) — it is <b>never</b> stored in the published app.</div>
335
-
<div class="card">
336
-
<div class="field"><label>Base URL ${info('The production endpoint. Baked in as the default; operators can override.')}</label>
() => `<div class="stephdr"><span class="k">Step ${step+1} of ${STEPS.length}</span><h2>Methods</h2></div>
345
347
<p class="desc" style="margin:-8px 0 16px">Each method is one call an agent can make. Give it a clear name, the backend route, a latency class, a description, and its parameters. The live preview shows exactly what agents will see and run.</p>
return `<div class="explain">We generate, sign, and verify an adapter that runs your command. The command must already be
427
+
installed on the operator's host. The child runs with a <b>scrubbed environment</b> — only the variables you list below
428
+
(plus PATH/HOME/locale) are passed through. The app ships with a <code>proc.exec</code> grant scoped to exactly this command, and installs <b>guarded</b> via the reviewed catalogue.</div>
429
+
<div class="card">
430
+
<div class="field"><label>Command ${info('The base argv the adapter runs. Comma-separated, e.g. gh —or— python, -m, tool. Each method appends its own args.')}</label>
<div class="field"><label>Env passthrough <span class="muted">(optional)</span> ${info('Host environment variables the CLI may see, comma-separated. Everything else is scrubbed from the child.')}</label>
<div class="field"><span class="ghead">Path ${info('Backend route. GET → params become the query string; POST → JSON body.')}</span><input class="m-path" placeholder="/search" value="${esc(m.http.path)}"></div>`;
441
+
}
442
+
// The CLI route lives in a full-width row below the name/latency grid.
443
+
function cliRouteRow(m){
444
+
const pass = !!m.cli.passthrough;
445
+
const enumerated = `<span class="ghead" style="margin-top:12px">Arguments ${info('Comma-separated argv appended to the command. Use ${param} to insert a payload field, e.g. current, --lat, ${lat}')}</span>
<label class="toggle" style="margin-top:8px"><input type="checkbox" class="m-flags" ${m.cli.params_as_flags?'checked':''}> <span>Also append each parameter as <code>--name value</code></span></label>`;
448
+
const passthru = `<p class="desc" style="margin:6px 0 0">Every subcommand is reachable; the caller supplies the argv. Call it as <code>{"args":["…"]}</code> — no baked arguments.</p>`;
<label class="toggle"><input type="checkbox" class="m-passthrough" ${pass?'checked':''}> <span>Passthrough — front the whole CLI (translate any <code><cli> <args></code>)</span></label>
<div class="field"><span class="ghead">Method name ${info('Prefixed with your namespace, e.g. '+esc(ns()||'app')+'.search')}</span><input class="m-name" placeholder="${esc(ns()||'app')}.search" value="${esc(m.name)}"></div>
<div class="field"><span class="ghead">Path ${info('Backend route. GET → params become the query string; POST → JSON body.')}</span><input class="m-path" placeholder="/search" value="${esc(m.http.path)}"></div>
462
+
${routeCells}
416
463
<div class="field"><span class="ghead">Latency ${info('How long a typical call takes — agents use it to pick the cheapest method that fits. Fast: under 5s · Medium: up to 15s · Slow: up to 1 min.')}</span><select class="m-latency"><option value="">Select…</option>${['fast','med','slow'].map(l=>`<option value="${l}" ${m.latency===l?'selected':''}>${l==='med'?'Medium':l[0].toUpperCase()+l.slice(1)}</option>`).join('')}</select></div>
417
464
</div>
465
+
${cli ? cliRouteRow(m) : ''}
418
466
<div class="field"><span class="ghead">Description ${info('Shown in the help doc agents read. Be specific about what it returns.')}</span>
419
467
<textarea class="m-description" placeholder="Search the corpus; returns ranked results with url, title, score.">${esc(m.description)}</textarea></div>
if(badm) bad('e-methods','Each method needs a '+ns()+'.-prefixed name, path starting with /, a latency, and a description'); else show('e-methods',''); }
if(badm) bad('e-methods','Each CLI method needs a '+ns()+'.-prefixed name, a latency, a description, and either arguments, params-as-flags, or passthrough'); else show('e-methods','');
if(badm) bad('e-methods','Each method needs a '+ns()+'.-prefixed name, path starting with /, a latency, and a description'); else show('e-methods','');
581
+
}
514
582
}
515
583
return ok;
516
584
}
@@ -534,14 +602,18 @@ function renderReview(){
534
602
const s=submission();
535
603
// Each value carries already-escaped HTML; the only markup allowed in a value
536
604
// is the <br> separators in Methods. All user-controlled text goes through esc().
0 commit comments