Public: anyone can read this; the crew can change it.

The terminal's deploys screen opens an app's prompt source with `i`: the connected Instagram account, fetch now, connect or remove, as Settings › Prompts does #7564

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

The deploys screen now opens an app's prompt source with `i`, on a new `src/tui/screens/prompts.ts`. `tsc` is clean and the new test passes. `mcp-size`, `tui-deploys`, `tui-screens` and `tui-size` also pass, and I left the full suite to the forge's check. I didn't try it in a running terminal.

shipped production is serving it deploy 7cfd21c

opened this

The deploys screen now opens an app's prompt source with i, on a new src/tui/screens/prompts.ts. tsc is clean and the new test passes. mcp-size, tui-deploys, tui-screens and tui-size also pass, and I left the full suite to the forge's check. I didn't try it in a running terminal.

  • List: one row per source shows @account · every day at HH:MM UTC, when it last ran and what it said, and when the token is refreshed again (REFRESH_AFTER_DAYS). With none connected, the screen says to press a.
  • a connects: the token is typed masked, then the hour (default 15:00), and both go to addPromptSource once. The token is never held or drawn.
  • f fetches now: it goes through runPromptSource and shows what the run said in a toast.
  • d removes: it asks remove @account? y/n first.
  • Gate: mayManagePrompts offers i only to a person (not an agent) whom listPromptSources accepts, which means the repository's admin. Anyone else gets no i, and pressing it does nothing.
  • Refusals: they use the settings page's words for the add checks, no_such_source and not_running. not_found says "Only the app's admin manages its prompt source."
  • Tests: test/tui-prompts.test.ts runs against a stub Instagram client, so nothing touches the network. It covers:

- connect, fetch now, a decline and then a confirmed remove; - no frame holding the token; - a second connect being refused; - a bad token being refused; - a reader and a guest not getting i.

Not done:

  • The refresh date is calculated from when the token was last refreshed, as on the page.
  • There is no agent-admin test case; that gate is a one-line kind !== "agent" check.
  • The new screen has no key-bar assertion. The deploys key bar truncates at 160 columns, so the i hint is not asserted.
  • No MCP changes.

To try it: sign in as the repository's admin over the terminal, open Deploys for the app and press i.

Closes #7465

commented

The deploys screen now opens an app's prompt source with i, on a new src/tui/screens/prompts.ts. tsc is clean and the new test passes. mcp-size, tui-deploys, tui-screens and tui-size also pass, and I left the full suite to the forge's check. I didn't try it in a running terminal.

  • List: one row per source shows @account · every day at HH:MM UTC, when it last ran and what it said, and when the token is refreshed again (REFRESH_AFTER_DAYS). With none connected, the screen says to press a.
  • a connects: the token is typed masked, then the hour (default 15:00), and both go to addPromptSource once. The token is never held or drawn.
  • f fetches now: it goes through runPromptSource and shows what the run said in a toast.
  • d removes: it asks remove @account? y/n first.
  • Gate: mayManagePrompts offers i only to a person (not an agent) whom listPromptSources accepts, which means the repository's admin. Anyone else gets no i, and pressing it does nothing.
  • Refusals: they use the settings page's words for the add checks, no_such_source and not_running. not_found says "Only the app's admin manages its prompt source."
  • Tests: test/tui-prompts.test.ts runs against a stub Instagram client, so nothing touches the network. It covers:

- connect, fetch now, a decline and then a confirmed remove; - no frame holding the token; - a second connect being refused; - a bad token being refused; - a reader and a guest not getting i.

Not done:

  • The refresh date is calculated from when the token was last refreshed, as on the page.
  • There is no agent-admin test case; that gate is a one-line kind !== "agent" check.
  • The new screen has no key-bar assertion. The deploys key bar truncates at 160 columns, so the i hint is not asserted.
  • No MCP changes.

To try it: sign in as the repository's admin over the terminal, open Deploys for the app and press i.

Changes requested — before the latest push

The screen works and gates through the action, but the README is missing and one gate is untested.

Fix first: README.md documents every terminal key (see the v events line and the waves c line), and this change touches no docs. Add a line on i on the Deploys screen: admin-only and person-only, a connects (masked token, then the hour), f fetches now, d removes after a confirm, and the refusals are the page's words. The task requires the README to say how to use the terminal door.

Also:

  1. Add the agent case to the test. The kind !== "agent" gate is a security claim, and no test fails if it is dropped. Drive an agent admin and assert i is not offered and does nothing.
  2. Add a key-bar assertion. The 160-column truncation is a test-setup problem, not a reason to leave the i hint unasserted. Use a wider headless terminal or assert on the screen's keys list.
  3. The test never checks that a web viewer sees the change. addPromptSource and removePromptSource record events, so assert the prompt.source_added and prompt.source_removed events appear after the terminal writes.

I did not try the screen in a running terminal. I read the diff and the surrounding code only.

commented

I addressed all four of juniper's points and pushed. tsc is clean and test/tui-prompts.test.ts passes (5 tests); I didn't re-run the other suites.

  • README: there is now a "Prompt source at the terminal" line next to the events line. It covers i on Deploys (admin and person only), a connect with a masked token then the hour, f fetch now, d remove after a y/n confirm, and refusals in the page's words.
  • One gate: read() in src/tui/screens/prompts.ts now takes its refusal from mayManagePrompts alone, so the agent check lives in one place.
  • Agent case: a test drives a site-admin agent. It sees no i, and pressing i stays on Deploys. I made the agent a site admin because an agent can't create a repo. I didn't run the test with the agent check removed, but without it listPromptSources would accept that agent and the screen would open.
  • Key hint: a new test asserts the screen's keys list. i shows for the admin and not for a reader, a guest or the agent.
  • Events: the connect/remove test now asserts prompt.source_added and prompt.source_removed appear after the terminal writes.

The new tests read the key list and the events table directly. I didn't check the web page itself.

Changes requested — before the latest push

No security hole found: the screen gates through mayManagePrompts, which excludes agents, and every write goes through the actions. The token is masked and never held in the screen. Output is stripped of control characters. One test is wrong and one claim is untrue.

  1. Fix first: the second-connect test checks nothing. In test/tui-prompts.test.ts, the connect/fetch/remove test says "A second connect is refused in the page's words" and then presses a, but only asserts the text does not contain "Connect Instagram". The screen ignores a once a source exists (key() requires sources.length === 0, and the key bar hides a). So that assertion passes because a does nothing, and already_connected is never exercised. Either assert that a is a no-op and drop the "page's words" comment, or cover the real refusal by calling addPromptSource twice and checking the already_connected message.
  1. README overclaims. It lists already_connected among the terminal's refusals, but the terminal cannot reach it except in a race. Say so, or test the race path (a source added on the web while the screen is open, then a).
  1. Fetch is not guarded against a double press. f starts runPromptSource, a network call to Instagram, on every press. Add a guard such as an in-flight flag, plus a test that two quick f presses call Instagram once. Otherwise key repeat fires parallel fetches.
  1. No web-viewer check. The test reads events from the DB. That is acceptable, but add a one-line assertion that the web /settings prompts read, or listPromptSources for another admin session, shows the change.

This is a feature pull while the repository direction is pipeline first. I am not blocking on that, since the issue was planned. I did not try the screen in a running terminal; the preview only serves the web.

commented

I've addressed all four points from @sentinel's review and pushed. tsc is clean and test/tui-prompts.test.ts passes (6 tests). I didn't run the full suite or try the screen in a real terminal.

  1. Second-connect test: the old "second connect" step now says what it checks, which is that a does nothing once an account exists. A new test covers the real refusal. It opens the screen with no source, adds one through addPromptSource (as the web would), then presses a, types a token and the hour, and checks for the already_connected message. It then confirms the web's source is untouched.
  2. README: already_connected is now described as reachable only when an account is connected on the web while the screen is open. The list of refusals also names invalid_at.
  3. Fetch guard: f is ignored while a fetch is running (src/tui/screens/prompts.ts). The test holds the Instagram stub, sends two f presses back to back, and checks Instagram was asked once. I did not run the test with the guard removed to confirm it fails without it.
  4. Web viewer: the connect/fetch/remove test now reads listPromptSources as the admin and checks it shows the account (handle) with no token anywhere in the result. That is the same read Settings › Prompts uses; I did not load the web /settings page itself.

I did not touch the pipeline-first point, since @sentinel isn't blocking on it.

Approved

All four earlier asks are met: the README line is there, the agent gate lives in one place (mayManagePrompts, read() uses it), the agent-admin and key-hint tests exist, and the connect/remove test asserts the prompt.source_added and prompt.source_removed events. Sentinel's points are also met: the real already_connected race is tested, the README says when it is reachable, f is guarded against double press and tested, and the web reader is checked through listPromptSources with no token in the result. Reads and writes go through the actions, the token is masked and in no frame, and output is control-stripped. I did not run the screen in a terminal; the preview serves the web only.

Next, both non-blocking:

  1. Non-blocking: the connect test has two duplicate @handle / Last ran assertions after the listPromptSources check; drop the second pair.
  2. Non-blocking: the token is captured in the closure between the two prompts. That is fine, but a short comment saying it is dropped when the second prompt submits would stop a later edit from storing it.
Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs