orizu 0.5.32 → 0.5.34
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/dist/app-commands.d.ts.map +1 -1
- package/dist/app-commands.js +14 -5
- package/dist/app-page-url.d.ts +9 -0
- package/dist/app-page-url.d.ts.map +1 -0
- package/dist/app-page-url.js +12 -0
- package/dist/archive-artifacts-cli.js +2 -2
- package/dist/hosted-agent-prompt.d.ts +1 -12
- package/dist/hosted-agent-prompt.d.ts.map +1 -1
- package/dist/hosted-agent-prompt.js +3 -1
- package/dist/hosted-loop.d.ts.map +1 -1
- package/dist/hosted-loop.js +8 -3
- package/dist/hosted-run-event-sink.d.ts +10 -1
- package/dist/hosted-run-event-sink.d.ts.map +1 -1
- package/dist/hosted-run-event-sink.js +69 -8
- package/package.json +1 -1
- package/src/app-commands.ts +14 -5
- package/src/app-page-url.ts +13 -0
- package/src/archive-artifacts-cli.ts +2 -2
- package/src/hosted-agent-prompt.ts +4 -1
- package/src/hosted-loop.ts +8 -3
- package/src/hosted-run-event-sink.ts +74 -9
- package/vendor/skills/orizu/references/cli-reference.md +2 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-commands.d.ts","sourceRoot":"","sources":["../src/app-commands.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"app-commands.d.ts","sourceRoot":"","sources":["../src/app-commands.ts"],"names":[],"mappings":"AAkCA,iBAAe,QAAQ,kBActB;AAED,iBAAS,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAU/C;AAED,iBAAS,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAUhD;AAED,iBAAS,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAY9D;AAED,iBAAS,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAElE;AAED,iBAAS,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAG1F;AAED,iBAAS,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAUjE;AAED,iBAAS,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAEjD;AAED,iBAAS,qBAAqB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,MAAM,GAAG;IACpF,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;CACb,CAeA;AAED,iBAAS,kBAAkB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,MAAM;;;;;IAoB/E;AAED,iBAAS,wBAAwB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,QAGjE;AAED,iBAAS,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEjE;AAED,iBAAe,iBAAiB,kBAiD/B;AAED,iBAAe,kBAAkB,kBAiDhC;AAED,iBAAe,iBAAiB,kBAiD/B;AAED,iBAAe,cAAc,kBAgD5B;AA+CD,iBAAe,SAAS,kBA2CvB;AAED,yFAAyF;AACzF,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,eAAe,EAAE,MAAM,GAAG,MAAM,CAK3G;AAED,iBAAe,eAAe,kBAmD7B;AAED,OAAO,EACL,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,qBAAqB,EACrB,kBAAkB,EAClB,gBAAgB,EAChB,QAAQ,EACR,gBAAgB,EAChB,wBAAwB,EACxB,SAAS,EACT,eAAe,EACf,QAAQ,EACR,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,cAAc,EACd,SAAS,EACT,eAAe,GAChB,CAAA"}
|
package/dist/app-commands.js
CHANGED
|
@@ -7,17 +7,24 @@ import { resolveProjectSlug, fetchApps, selectAppIdInteractively } from './accou
|
|
|
7
7
|
import { printJson, printLine, getErrorMessage } from './cli-console-output.js';
|
|
8
8
|
import { printAppSummaries } from './archive-artifacts-cli.js';
|
|
9
9
|
import { isNodeError, formatTerminalLink } from './auth-commands.js';
|
|
10
|
-
import { authedFetch } from './http.js';
|
|
11
|
-
import {
|
|
10
|
+
import { authedFetch, getBaseUrl } from './http.js';
|
|
11
|
+
import { appPageUrl } from './app-page-url.js';
|
|
12
|
+
import { parseJsonResponse, sanitizeHumanInlineText, sanitizeTerminalText } from './json-response.js';
|
|
12
13
|
import { runLocalAppPreview } from './preview-runtime.js';
|
|
13
14
|
async function listApps() {
|
|
14
15
|
const project = getArg('--project') || await resolveProjectSlug(null);
|
|
15
|
-
const
|
|
16
|
+
const baseUrl = getBaseUrl();
|
|
17
|
+
const apps = (await fetchApps(project, getArchiveListStatus('apps')))
|
|
18
|
+
.map(app => ({ ...app, pageUrl: appPageUrl(baseUrl, app.id) }));
|
|
16
19
|
if (hasJsonFlag()) {
|
|
17
20
|
printJson({ apps });
|
|
18
21
|
return;
|
|
19
22
|
}
|
|
20
23
|
printAppSummaries(apps, printLine);
|
|
24
|
+
// An archived app's page still renders as if live, so it gets no link line.
|
|
25
|
+
apps
|
|
26
|
+
.filter(app => app.status !== 'archived')
|
|
27
|
+
.forEach(app => printLine(`Link: ${sanitizeHumanInlineText(sanitizeTerminalText, app.name || app.id)} ${formatTerminalLink(app.pageUrl)}`));
|
|
21
28
|
}
|
|
22
29
|
function readSourceFile(pathArg) {
|
|
23
30
|
const expandedPath = expandHomePath(pathArg);
|
|
@@ -152,7 +159,7 @@ async function createAppFromFile() {
|
|
|
152
159
|
}
|
|
153
160
|
const data = await parseJsonResponse(response, 'App create');
|
|
154
161
|
if (hasJsonFlag()) {
|
|
155
|
-
printJson({ app: data.app, warnings: data.warnings || [] });
|
|
162
|
+
printJson({ app: { ...data.app, pageUrl: data.app.url || appPageUrl(getBaseUrl(), data.app.id) }, warnings: data.warnings || [] });
|
|
156
163
|
return;
|
|
157
164
|
}
|
|
158
165
|
printLine(`Created app ${sanitizeTerminalText(data.app.name)} (${sanitizeTerminalText(data.app.id)}) v${data.app.versionNum}`);
|
|
@@ -240,11 +247,13 @@ async function updateAppFromFile() {
|
|
|
240
247
|
throw new Error(`Failed to update app: ${await response.text()}`);
|
|
241
248
|
}
|
|
242
249
|
const data = await parseJsonResponse(response, 'App update');
|
|
250
|
+
const pageUrl = appPageUrl(getBaseUrl(), data.app.id);
|
|
243
251
|
if (hasJsonFlag()) {
|
|
244
|
-
printJson({ app: data.app, warnings: data.warnings || [] });
|
|
252
|
+
printJson({ app: { ...data.app, pageUrl }, warnings: data.warnings || [] });
|
|
245
253
|
return;
|
|
246
254
|
}
|
|
247
255
|
printLine(`Updated app ${sanitizeTerminalText(data.app.name)} (${sanitizeTerminalText(data.app.id)}) to v${data.app.versionNum}`);
|
|
256
|
+
printLine(`View app: ${formatTerminalLink(pageUrl)}`);
|
|
248
257
|
if (data.warnings?.length) {
|
|
249
258
|
printLine(`Warnings: ${sanitizeTerminalText(data.warnings.join('; '))}`);
|
|
250
259
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where an app's page lives in the web app (ORI-2342): `/preview/<app id>`,
|
|
3
|
+
* the route in apps/web/app/preview/[id]/page.tsx. `orizu apps list`,
|
|
4
|
+
* `orizu apps update` and the hosted agent's prompt build their links from
|
|
5
|
+
* here. `orizu apps create` prints the link the server returns instead.
|
|
6
|
+
*/
|
|
7
|
+
export declare function appPagePath(appId: string): string;
|
|
8
|
+
export declare function appPageUrl(baseUrl: string, appId: string): string;
|
|
9
|
+
//# sourceMappingURL=app-page-url.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-page-url.d.ts","sourceRoot":"","sources":["../src/app-page-url.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEjE"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where an app's page lives in the web app (ORI-2342): `/preview/<app id>`,
|
|
3
|
+
* the route in apps/web/app/preview/[id]/page.tsx. `orizu apps list`,
|
|
4
|
+
* `orizu apps update` and the hosted agent's prompt build their links from
|
|
5
|
+
* here. `orizu apps create` prints the link the server returns instead.
|
|
6
|
+
*/
|
|
7
|
+
export function appPagePath(appId) {
|
|
8
|
+
return `/preview/${appId}`;
|
|
9
|
+
}
|
|
10
|
+
export function appPageUrl(baseUrl, appId) {
|
|
11
|
+
return `${baseUrl.replace(/\/+$/, '')}${appPagePath(encodeURIComponent(appId))}`;
|
|
12
|
+
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { authedFetch } from './http.js';
|
|
9
9
|
import { extractErrorMessage } from './error-response.js';
|
|
10
|
-
import { sanitizeTerminalText } from './json-response.js';
|
|
10
|
+
import { sanitizeHumanInlineText, sanitizeTerminalText } from './json-response.js';
|
|
11
11
|
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
12
12
|
const ARTIFACT_TYPES = {
|
|
13
13
|
app: 'app',
|
|
@@ -61,7 +61,7 @@ export function printAppSummaries(items, print) {
|
|
|
61
61
|
return print('No apps found.');
|
|
62
62
|
printTable(['APP ID', 'APP NAME', 'VERSION', 'ARCHIVE'], items.map(item => [
|
|
63
63
|
sanitizeTerminalText(item.id),
|
|
64
|
-
sanitizeTerminalText
|
|
64
|
+
sanitizeHumanInlineText(sanitizeTerminalText, item.name || '-'),
|
|
65
65
|
`v${item.currentVersionNum || 1}`,
|
|
66
66
|
sanitizeTerminalText(item.status || 'active'),
|
|
67
67
|
]), print);
|
|
@@ -1,14 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The system prompt for the hosted agent (ORI-2207).
|
|
3
|
-
*
|
|
4
|
-
* OpenCode (the program that runs the agent in a hosted session) ships its own
|
|
5
|
-
* built-in prompt that starts "You are opencode, an interactive CLI tool…", so
|
|
6
|
-
* the agent used to introduce itself as OpenCode. We register our own "primary
|
|
7
|
-
* agent" in the OpenCode config with this text, and every prompt request picks
|
|
8
|
-
* it by name. That replaces OpenCode's built-in prompt entirely, so this text
|
|
9
|
-
* carries the working rules we still want: use the tools, keep answers short.
|
|
10
|
-
* AGENTS.md is still added after it, as before.
|
|
11
|
-
*/
|
|
12
1
|
export declare const HOSTED_AGENT_NAME = "orizu";
|
|
13
|
-
export declare const HOSTED_AGENT_PROMPT
|
|
2
|
+
export declare const HOSTED_AGENT_PROMPT: string;
|
|
14
3
|
//# sourceMappingURL=hosted-agent-prompt.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hosted-agent-prompt.d.ts","sourceRoot":"","sources":["../src/hosted-agent-prompt.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"hosted-agent-prompt.d.ts","sourceRoot":"","sources":["../src/hosted-agent-prompt.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,iBAAiB,UAAU,CAAA;AAExC,eAAO,MAAM,mBAAmB,QAK4Z,CAAA"}
|
|
@@ -9,9 +9,11 @@
|
|
|
9
9
|
* carries the working rules we still want: use the tools, keep answers short.
|
|
10
10
|
* AGENTS.md is still added after it, as before.
|
|
11
11
|
*/
|
|
12
|
+
import { appPagePath } from './app-page-url.js';
|
|
12
13
|
export const HOSTED_AGENT_NAME = 'orizu';
|
|
13
14
|
export const HOSTED_AGENT_PROMPT = `You are the Orizu agent. You work in the person's repository inside an Orizu workbench, helping them with software engineering tasks. If asked who or what you are, say you are the Orizu agent. Never call yourself OpenCode.
|
|
14
15
|
|
|
15
16
|
- Do the work with your tools: read files, search, edit, and run commands. Do not guess at what a file contains when you can look.
|
|
16
17
|
- Keep answers short and plain. Say what you did and what the person needs to know next.
|
|
17
|
-
- Follow the repository's own conventions and any instructions in its AGENTS.md
|
|
18
|
+
- Follow the repository's own conventions and any instructions in its AGENTS.md.
|
|
19
|
+
- An Orizu app's page link is <Orizu server>${appPagePath('<app id>')}, where <Orizu server> is the server the orizu CLI talks to (https://app.orizu.ai by default). To give someone an app's link, run \`orizu projects list\` to get the \`<team>/<project>\` slug, then \`orizu apps list --project <team>/<project>\`: it prints each app's full link. The --project flag is required here: prompts cannot run in this environment. Never guess a link.`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hosted-loop.d.ts","sourceRoot":"","sources":["../src/hosted-loop.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"hosted-loop.d.ts","sourceRoot":"","sources":["../src/hosted-loop.ts"],"names":[],"mappings":"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"}
|
package/dist/hosted-loop.js
CHANGED
|
@@ -691,7 +691,10 @@ export async function runHostedLoop(opts) {
|
|
|
691
691
|
await sleep(delayMs);
|
|
692
692
|
try {
|
|
693
693
|
await sink.finish(error.intendedStatus, error.finishOptions);
|
|
694
|
-
|
|
694
|
+
// A timer flush may have met the server's 404/410 during the sleep,
|
|
695
|
+
// making finish() a no-op: honour the server, as the drain does.
|
|
696
|
+
const status = sink.terminatedServerSide ? 'cancelled' : error.intendedStatus;
|
|
697
|
+
return { status, agentSessionId, installOk, error: null };
|
|
695
698
|
}
|
|
696
699
|
catch (retryError) {
|
|
697
700
|
// The run went terminal server-side while we were retrying: accept
|
|
@@ -705,8 +708,10 @@ export async function runHostedLoop(opts) {
|
|
|
705
708
|
}
|
|
706
709
|
if (sink.sealed) {
|
|
707
710
|
// A concurrent/preceding delivery landed after all — the record is
|
|
708
|
-
// terminal server-side with our intended status semantics preserved
|
|
709
|
-
|
|
711
|
+
// terminal server-side with our intended status semantics preserved —
|
|
712
|
+
// unless a timer flush met the server's 404/410 instead.
|
|
713
|
+
const status = sink.terminatedServerSide ? 'cancelled' : error.intendedStatus;
|
|
714
|
+
return { status, agentSessionId, installOk, error: null };
|
|
710
715
|
}
|
|
711
716
|
return {
|
|
712
717
|
status: error.intendedStatus,
|
|
@@ -66,12 +66,15 @@ export interface RunEventSink {
|
|
|
66
66
|
* Terminal kinds are rejected (they must go through `finish`). */
|
|
67
67
|
append(event: HarnessEvent): Promise<void>;
|
|
68
68
|
/** Force-flush any coalesced token snapshots (also runs on every structural
|
|
69
|
-
* event
|
|
69
|
+
* event, on `finish`, and every `tokenFlushMs` while new tokens arrive). */
|
|
70
70
|
flushTokens(): Promise<void>;
|
|
71
71
|
/** Terminal transition via PATCH; records the transcript tail first, then CAS. */
|
|
72
72
|
finish(status: TerminalStatus, opts?: FinishOptions): Promise<void>;
|
|
73
73
|
/** True once a terminal transition has landed and the sink is sealed. */
|
|
74
74
|
readonly sealed: boolean;
|
|
75
|
+
/** True when an append met 404/410, i.e. the server ended the run, not this
|
|
76
|
+
* writer's PATCH. A timer flush can learn this with no caller awaiting it. */
|
|
77
|
+
readonly terminatedServerSide: boolean;
|
|
75
78
|
/** The next sequence the sink will attempt to allocate (test/resume aid). */
|
|
76
79
|
readonly nextSequence: number;
|
|
77
80
|
/** Events the sink has confirmed appended, in order (redacted payloads). */
|
|
@@ -163,7 +166,13 @@ export interface CreateRunEventSinkOptions {
|
|
|
163
166
|
sleepImpl?: (ms: number) => Promise<void>;
|
|
164
167
|
/** Local diagnostic sink for disagreement-rule logs (never throws). */
|
|
165
168
|
onDiagnostic?: (message: string) => void;
|
|
169
|
+
/** How often buffered token snapshots are flushed while a reply is being
|
|
170
|
+
* written (ORI-2340). Default TOKEN_FLUSH_MS. */
|
|
171
|
+
tokenFlushMs?: number;
|
|
166
172
|
}
|
|
173
|
+
/** A text-only reply reaches the browser as growing snapshots at this cadence
|
|
174
|
+
* instead of whole at the end of the turn (ORI-2340). */
|
|
175
|
+
export declare const TOKEN_FLUSH_MS = 250;
|
|
167
176
|
export declare function createRunEventSink(options: CreateRunEventSinkOptions): RunEventSink;
|
|
168
177
|
export interface ResumeRunEventSinkOptions extends Omit<CreateRunEventSinkOptions, 'startSequence'> {
|
|
169
178
|
/** Page size for the cursor tail while discovering the latest sequence. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hosted-run-event-sink.d.ts","sourceRoot":"","sources":["../src/hosted-run-event-sink.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAIzE,MAAM,MAAM,WAAW,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;AAUhF,eAAO,MAAM,yBAAyB,mBAAmB,CAAA;AACzD,eAAO,MAAM,yBAAyB,mBAAmB,CAAA;AAiCzD;oFACoF;AACpF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,GAAG,IAAI,CAEtE;AAED,eAAO,MAAM,sBAAsB,gBAAgB,CAAA;AACnD,eAAO,MAAM,2BAA2B,qBAAqB,CAAA;AAC7D,gFAAgF;AAChF,eAAO,MAAM,yBAAyB,QAAS,CAAA;AAY/C,eAAO,MAAM,6BAA6B,uBAAuB,CAAA;AACjE,eAAO,MAAM,iCAAiC,2BAA2B,CAAA;AAEzE,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,CAAA;AAEjE,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAClC;8EAC0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B;uEACmE;IACnE,MAAM,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1C;
|
|
1
|
+
{"version":3,"file":"hosted-run-event-sink.d.ts","sourceRoot":"","sources":["../src/hosted-run-event-sink.ts"],"names":[],"mappings":"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"}
|
|
@@ -156,6 +156,9 @@ const TERMINAL_STATUSES = new Set(['succeeded', 'failed', 'cancelled']);
|
|
|
156
156
|
function isTerminalStatus(status) {
|
|
157
157
|
return status !== null && TERMINAL_STATUSES.has(status);
|
|
158
158
|
}
|
|
159
|
+
/** A text-only reply reaches the browser as growing snapshots at this cadence
|
|
160
|
+
* instead of whole at the end of the turn (ORI-2340). */
|
|
161
|
+
export const TOKEN_FLUSH_MS = 250; // held equal to SESSION_TIMERS.agentTokenFlushMs by test
|
|
159
162
|
const DEFAULT_MAX_CRITICAL_ATTEMPTS = 4;
|
|
160
163
|
// Non-critical appends get a SMALL bounded retry so a single transient blip
|
|
161
164
|
// (network reset / 5xx) does not drop the event — but on exhaustion they are
|
|
@@ -209,6 +212,53 @@ export function createRunEventSink(options) {
|
|
|
209
212
|
const tokenBuffer = new Map();
|
|
210
213
|
let nextSequence = options.startSequence ?? 1;
|
|
211
214
|
let sealed = false;
|
|
215
|
+
let terminatedServerSide = false;
|
|
216
|
+
// One promise chain for every buffer-touching operation: append, flushTokens,
|
|
217
|
+
// finish and the timer flush below. This is how the timer honours the
|
|
218
|
+
// reentrancy contract on flushTokens — it queues behind an in-flight append
|
|
219
|
+
// or flush instead of running beside it. A rejected step reaches its own
|
|
220
|
+
// caller and never breaks the chain.
|
|
221
|
+
let queue = Promise.resolve();
|
|
222
|
+
function serialize(fn) {
|
|
223
|
+
const run = queue.then(fn);
|
|
224
|
+
queue = run.catch(() => { });
|
|
225
|
+
return run;
|
|
226
|
+
}
|
|
227
|
+
// ORI-2340 timer flush: started by the first token, unref'd so it never keeps
|
|
228
|
+
// the process alive, stopped for good once the sink seals or the bearer is
|
|
229
|
+
// refused. `tokensDirty` keeps a quiet tick (or a dropped snapshot with no
|
|
230
|
+
// newer text) from posting again.
|
|
231
|
+
let flushTimer = null;
|
|
232
|
+
let flushTimerStopped = false;
|
|
233
|
+
let tokensDirty = false;
|
|
234
|
+
let timerFlushQueued = false;
|
|
235
|
+
function stopFlushTimer() {
|
|
236
|
+
flushTimerStopped = true;
|
|
237
|
+
if (flushTimer)
|
|
238
|
+
clearInterval(flushTimer);
|
|
239
|
+
flushTimer = null;
|
|
240
|
+
}
|
|
241
|
+
function onFlushTick() {
|
|
242
|
+
if (sealed)
|
|
243
|
+
return stopFlushTimer();
|
|
244
|
+
if (!tokensDirty || timerFlushQueued)
|
|
245
|
+
return;
|
|
246
|
+
timerFlushQueued = true;
|
|
247
|
+
serialize(async () => {
|
|
248
|
+
timerFlushQueued = false;
|
|
249
|
+
// Queued behind finish(), the sink may have sealed since the tick fired.
|
|
250
|
+
if (sealed)
|
|
251
|
+
return;
|
|
252
|
+
tokensDirty = false;
|
|
253
|
+
await flushTokens();
|
|
254
|
+
}).catch(error => {
|
|
255
|
+
// A refused bearer would be refused again every tick; the loop learns of
|
|
256
|
+
// it at its next structural event or finish.
|
|
257
|
+
if (error instanceof RunAuthError)
|
|
258
|
+
stopFlushTimer();
|
|
259
|
+
diag(`timer token flush failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
260
|
+
});
|
|
261
|
+
}
|
|
212
262
|
function assertWritable() {
|
|
213
263
|
if (sealed) {
|
|
214
264
|
throw new RunTerminalError('run is terminal server-side — refusing to append (Orizu records win)');
|
|
@@ -280,6 +330,7 @@ export function createRunEventSink(options) {
|
|
|
280
330
|
// Run gone/terminal server-side: seal and stop (Orizu records win).
|
|
281
331
|
if (response.status === 404 || response.status === 410) {
|
|
282
332
|
sealed = true;
|
|
333
|
+
terminatedServerSide = true;
|
|
283
334
|
diag(`append rejected (${response.status}) for ${eventType} — sealing sink (run terminal)`);
|
|
284
335
|
throw new RunTerminalError(`append rejected (${response.status}) — run is terminal server-side`);
|
|
285
336
|
}
|
|
@@ -321,10 +372,10 @@ export function createRunEventSink(options) {
|
|
|
321
372
|
// REENTRANCY CONTRACT: flushTokens MUST run to completion before the next
|
|
322
373
|
// append()/flushTokens() begins — it is NOT safe to run concurrently with a
|
|
323
374
|
// token append. Coalescing correctness (and the `===` re-check below) relies on
|
|
324
|
-
//
|
|
325
|
-
//
|
|
326
|
-
//
|
|
327
|
-
//
|
|
375
|
+
// every append/flush running strictly sequentially, so no newer delta can
|
|
376
|
+
// mutate `tokenBuffer` mid-flush. The public methods and the ORI-2340 timer
|
|
377
|
+
// flush all go through `serialize` to keep that true; any new caller of this
|
|
378
|
+
// inner function MUST too, or the drop-preserving guarantee breaks.
|
|
328
379
|
async function flushTokens() {
|
|
329
380
|
if (tokenBuffer.size === 0)
|
|
330
381
|
return;
|
|
@@ -354,6 +405,11 @@ export function createRunEventSink(options) {
|
|
|
354
405
|
const messageId = event.messageId ?? '_';
|
|
355
406
|
const text = typeof event.payload.text === 'string' ? event.payload.text : '';
|
|
356
407
|
tokenBuffer.set(messageId, text);
|
|
408
|
+
tokensDirty = true;
|
|
409
|
+
if (!flushTimer && !flushTimerStopped) {
|
|
410
|
+
flushTimer = setInterval(onFlushTick, options.tokenFlushMs ?? TOKEN_FLUSH_MS);
|
|
411
|
+
flushTimer.unref?.();
|
|
412
|
+
}
|
|
357
413
|
return;
|
|
358
414
|
}
|
|
359
415
|
const eventType = eventTypeForKind(event.kind);
|
|
@@ -470,12 +526,15 @@ export function createRunEventSink(options) {
|
|
|
470
526
|
}
|
|
471
527
|
}
|
|
472
528
|
return {
|
|
473
|
-
append,
|
|
474
|
-
flushTokens,
|
|
475
|
-
finish,
|
|
529
|
+
append: event => serialize(() => append(event)),
|
|
530
|
+
flushTokens: () => serialize(flushTokens),
|
|
531
|
+
finish: (status, opts) => serialize(() => finish(status, opts)),
|
|
476
532
|
get sealed() {
|
|
477
533
|
return sealed;
|
|
478
534
|
},
|
|
535
|
+
get terminatedServerSide() {
|
|
536
|
+
return terminatedServerSide;
|
|
537
|
+
},
|
|
479
538
|
get nextSequence() {
|
|
480
539
|
return nextSequence;
|
|
481
540
|
},
|
|
@@ -583,7 +642,9 @@ export async function drainHarnessToSink(events, sink, opts = {}) {
|
|
|
583
642
|
: finishOpts;
|
|
584
643
|
try {
|
|
585
644
|
await sink.finish(status, mergedFinishOpts);
|
|
586
|
-
|
|
645
|
+
// A timer flush queued ahead of finish() can meet the server's 404/410
|
|
646
|
+
// after the check above; finish() then no-ops, so honour the server.
|
|
647
|
+
return sink.terminatedServerSide ? 'cancelled' : status;
|
|
587
648
|
}
|
|
588
649
|
catch (error) {
|
|
589
650
|
if (error instanceof RunTerminalError)
|
package/package.json
CHANGED
package/src/app-commands.ts
CHANGED
|
@@ -25,20 +25,27 @@ import { resolveProjectSlug, fetchApps, selectAppIdInteractively } from './accou
|
|
|
25
25
|
import { printJson, printLine, getErrorMessage } from './cli-console-output.js'
|
|
26
26
|
import { printAppSummaries } from './archive-artifacts-cli.js'
|
|
27
27
|
import { isNodeError, formatTerminalLink } from './auth-commands.js'
|
|
28
|
-
import { authedFetch } from './http.js'
|
|
29
|
-
import {
|
|
28
|
+
import { authedFetch, getBaseUrl } from './http.js'
|
|
29
|
+
import { appPageUrl } from './app-page-url.js'
|
|
30
|
+
import { parseJsonResponse, sanitizeHumanInlineText, sanitizeTerminalText } from './json-response.js'
|
|
30
31
|
import { runLocalAppPreview } from './preview-runtime.js'
|
|
31
32
|
|
|
32
33
|
|
|
33
34
|
|
|
34
35
|
async function listApps() {
|
|
35
36
|
const project = getArg('--project') || await resolveProjectSlug(null)
|
|
36
|
-
const
|
|
37
|
+
const baseUrl = getBaseUrl()
|
|
38
|
+
const apps = (await fetchApps(project, getArchiveListStatus('apps')))
|
|
39
|
+
.map(app => ({ ...app, pageUrl: appPageUrl(baseUrl, app.id) }))
|
|
37
40
|
if (hasJsonFlag()) {
|
|
38
41
|
printJson({ apps })
|
|
39
42
|
return
|
|
40
43
|
}
|
|
41
44
|
printAppSummaries(apps, printLine)
|
|
45
|
+
// An archived app's page still renders as if live, so it gets no link line.
|
|
46
|
+
apps
|
|
47
|
+
.filter(app => app.status !== 'archived')
|
|
48
|
+
.forEach(app => printLine(`Link: ${sanitizeHumanInlineText(sanitizeTerminalText, app.name || app.id)} ${formatTerminalLink(app.pageUrl)}`))
|
|
42
49
|
}
|
|
43
50
|
|
|
44
51
|
function readSourceFile(pathArg: string): string {
|
|
@@ -195,7 +202,7 @@ async function createAppFromFile() {
|
|
|
195
202
|
warnings?: string[]
|
|
196
203
|
}>(response, 'App create')
|
|
197
204
|
if (hasJsonFlag()) {
|
|
198
|
-
printJson({ app: data.app, warnings: data.warnings || [] })
|
|
205
|
+
printJson({ app: { ...data.app, pageUrl: data.app.url || appPageUrl(getBaseUrl(), data.app.id) }, warnings: data.warnings || [] })
|
|
199
206
|
return
|
|
200
207
|
}
|
|
201
208
|
printLine(`Created app ${sanitizeTerminalText(data.app.name)} (${sanitizeTerminalText(data.app.id)}) v${data.app.versionNum}`)
|
|
@@ -297,11 +304,13 @@ async function updateAppFromFile() {
|
|
|
297
304
|
app: { id: string; name: string; versionNum: number; componentName?: string }
|
|
298
305
|
warnings?: string[]
|
|
299
306
|
}>(response, 'App update')
|
|
307
|
+
const pageUrl = appPageUrl(getBaseUrl(), data.app.id)
|
|
300
308
|
if (hasJsonFlag()) {
|
|
301
|
-
printJson({ app: data.app, warnings: data.warnings || [] })
|
|
309
|
+
printJson({ app: { ...data.app, pageUrl }, warnings: data.warnings || [] })
|
|
302
310
|
return
|
|
303
311
|
}
|
|
304
312
|
printLine(`Updated app ${sanitizeTerminalText(data.app.name)} (${sanitizeTerminalText(data.app.id)}) to v${data.app.versionNum}`)
|
|
313
|
+
printLine(`View app: ${formatTerminalLink(pageUrl)}`)
|
|
305
314
|
if (data.warnings?.length) {
|
|
306
315
|
printLine(`Warnings: ${sanitizeTerminalText(data.warnings.join('; '))}`)
|
|
307
316
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where an app's page lives in the web app (ORI-2342): `/preview/<app id>`,
|
|
3
|
+
* the route in apps/web/app/preview/[id]/page.tsx. `orizu apps list`,
|
|
4
|
+
* `orizu apps update` and the hosted agent's prompt build their links from
|
|
5
|
+
* here. `orizu apps create` prints the link the server returns instead.
|
|
6
|
+
*/
|
|
7
|
+
export function appPagePath(appId: string): string {
|
|
8
|
+
return `/preview/${appId}`
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function appPageUrl(baseUrl: string, appId: string): string {
|
|
12
|
+
return `${baseUrl.replace(/\/+$/, '')}${appPagePath(encodeURIComponent(appId))}`
|
|
13
|
+
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import { authedFetch } from './http.js'
|
|
10
10
|
import { extractErrorMessage } from './error-response.js'
|
|
11
|
-
import { sanitizeTerminalText } from './json-response.js'
|
|
11
|
+
import { sanitizeHumanInlineText, sanitizeTerminalText } from './json-response.js'
|
|
12
12
|
|
|
13
13
|
type ArchiveStatus = 'active' | 'archived' | 'all'
|
|
14
14
|
type ArchiveAction = 'archive' | 'restore'
|
|
@@ -148,7 +148,7 @@ export function printAppSummaries(items: AppSummary[], print: PrintLine) {
|
|
|
148
148
|
['APP ID', 'APP NAME', 'VERSION', 'ARCHIVE'],
|
|
149
149
|
items.map(item => [
|
|
150
150
|
sanitizeTerminalText(item.id),
|
|
151
|
-
sanitizeTerminalText
|
|
151
|
+
sanitizeHumanInlineText(sanitizeTerminalText, item.name || '-'),
|
|
152
152
|
`v${item.currentVersionNum || 1}`,
|
|
153
153
|
sanitizeTerminalText(item.status || 'active'),
|
|
154
154
|
]),
|
|
@@ -9,10 +9,13 @@
|
|
|
9
9
|
* carries the working rules we still want: use the tools, keep answers short.
|
|
10
10
|
* AGENTS.md is still added after it, as before.
|
|
11
11
|
*/
|
|
12
|
+
import { appPagePath } from './app-page-url.js'
|
|
13
|
+
|
|
12
14
|
export const HOSTED_AGENT_NAME = 'orizu'
|
|
13
15
|
|
|
14
16
|
export const HOSTED_AGENT_PROMPT = `You are the Orizu agent. You work in the person's repository inside an Orizu workbench, helping them with software engineering tasks. If asked who or what you are, say you are the Orizu agent. Never call yourself OpenCode.
|
|
15
17
|
|
|
16
18
|
- Do the work with your tools: read files, search, edit, and run commands. Do not guess at what a file contains when you can look.
|
|
17
19
|
- Keep answers short and plain. Say what you did and what the person needs to know next.
|
|
18
|
-
- Follow the repository's own conventions and any instructions in its AGENTS.md
|
|
20
|
+
- Follow the repository's own conventions and any instructions in its AGENTS.md.
|
|
21
|
+
- An Orizu app's page link is <Orizu server>${appPagePath('<app id>')}, where <Orizu server> is the server the orizu CLI talks to (https://app.orizu.ai by default). To give someone an app's link, run \`orizu projects list\` to get the \`<team>/<project>\` slug, then \`orizu apps list --project <team>/<project>\`: it prints each app's full link. The --project flag is required here: prompts cannot run in this environment. Never guess a link.`
|
package/src/hosted-loop.ts
CHANGED
|
@@ -860,7 +860,10 @@ export async function runHostedLoop(opts: RunHostedLoopOptions): Promise<HostedL
|
|
|
860
860
|
await sleep(delayMs)
|
|
861
861
|
try {
|
|
862
862
|
await sink.finish(error.intendedStatus, error.finishOptions)
|
|
863
|
-
|
|
863
|
+
// A timer flush may have met the server's 404/410 during the sleep,
|
|
864
|
+
// making finish() a no-op: honour the server, as the drain does.
|
|
865
|
+
const status = sink.terminatedServerSide ? 'cancelled' : error.intendedStatus
|
|
866
|
+
return { status, agentSessionId, installOk, error: null }
|
|
864
867
|
} catch (retryError) {
|
|
865
868
|
// The run went terminal server-side while we were retrying: accept
|
|
866
869
|
// the server record (Orizu records win) instead of retrying further.
|
|
@@ -873,8 +876,10 @@ export async function runHostedLoop(opts: RunHostedLoopOptions): Promise<HostedL
|
|
|
873
876
|
}
|
|
874
877
|
if (sink.sealed) {
|
|
875
878
|
// A concurrent/preceding delivery landed after all — the record is
|
|
876
|
-
// terminal server-side with our intended status semantics preserved
|
|
877
|
-
|
|
879
|
+
// terminal server-side with our intended status semantics preserved —
|
|
880
|
+
// unless a timer flush met the server's 404/410 instead.
|
|
881
|
+
const status = sink.terminatedServerSide ? 'cancelled' : error.intendedStatus
|
|
882
|
+
return { status, agentSessionId, installOk, error: null }
|
|
878
883
|
}
|
|
879
884
|
return {
|
|
880
885
|
status: error.intendedStatus,
|
|
@@ -128,12 +128,15 @@ export interface RunEventSink {
|
|
|
128
128
|
* Terminal kinds are rejected (they must go through `finish`). */
|
|
129
129
|
append(event: HarnessEvent): Promise<void>
|
|
130
130
|
/** Force-flush any coalesced token snapshots (also runs on every structural
|
|
131
|
-
* event
|
|
131
|
+
* event, on `finish`, and every `tokenFlushMs` while new tokens arrive). */
|
|
132
132
|
flushTokens(): Promise<void>
|
|
133
133
|
/** Terminal transition via PATCH; records the transcript tail first, then CAS. */
|
|
134
134
|
finish(status: TerminalStatus, opts?: FinishOptions): Promise<void>
|
|
135
135
|
/** True once a terminal transition has landed and the sink is sealed. */
|
|
136
136
|
readonly sealed: boolean
|
|
137
|
+
/** True when an append met 404/410, i.e. the server ended the run, not this
|
|
138
|
+
* writer's PATCH. A timer flush can learn this with no caller awaiting it. */
|
|
139
|
+
readonly terminatedServerSide: boolean
|
|
137
140
|
/** The next sequence the sink will attempt to allocate (test/resume aid). */
|
|
138
141
|
readonly nextSequence: number
|
|
139
142
|
/** Events the sink has confirmed appended, in order (redacted payloads). */
|
|
@@ -251,8 +254,15 @@ export interface CreateRunEventSinkOptions {
|
|
|
251
254
|
sleepImpl?: (ms: number) => Promise<void>
|
|
252
255
|
/** Local diagnostic sink for disagreement-rule logs (never throws). */
|
|
253
256
|
onDiagnostic?: (message: string) => void
|
|
257
|
+
/** How often buffered token snapshots are flushed while a reply is being
|
|
258
|
+
* written (ORI-2340). Default TOKEN_FLUSH_MS. */
|
|
259
|
+
tokenFlushMs?: number
|
|
254
260
|
}
|
|
255
261
|
|
|
262
|
+
/** A text-only reply reaches the browser as growing snapshots at this cadence
|
|
263
|
+
* instead of whole at the end of the turn (ORI-2340). */
|
|
264
|
+
export const TOKEN_FLUSH_MS = 250 // held equal to SESSION_TIMERS.agentTokenFlushMs by test
|
|
265
|
+
|
|
256
266
|
const DEFAULT_MAX_CRITICAL_ATTEMPTS = 4
|
|
257
267
|
// Non-critical appends get a SMALL bounded retry so a single transient blip
|
|
258
268
|
// (network reset / 5xx) does not drop the event — but on exhaustion they are
|
|
@@ -312,6 +322,50 @@ export function createRunEventSink(options: CreateRunEventSinkOptions): RunEvent
|
|
|
312
322
|
|
|
313
323
|
let nextSequence = options.startSequence ?? 1
|
|
314
324
|
let sealed = false
|
|
325
|
+
let terminatedServerSide = false
|
|
326
|
+
|
|
327
|
+
// One promise chain for every buffer-touching operation: append, flushTokens,
|
|
328
|
+
// finish and the timer flush below. This is how the timer honours the
|
|
329
|
+
// reentrancy contract on flushTokens — it queues behind an in-flight append
|
|
330
|
+
// or flush instead of running beside it. A rejected step reaches its own
|
|
331
|
+
// caller and never breaks the chain.
|
|
332
|
+
let queue: Promise<unknown> = Promise.resolve()
|
|
333
|
+
function serialize<T>(fn: () => Promise<T>): Promise<T> {
|
|
334
|
+
const run = queue.then(fn)
|
|
335
|
+
queue = run.catch(() => {})
|
|
336
|
+
return run
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// ORI-2340 timer flush: started by the first token, unref'd so it never keeps
|
|
340
|
+
// the process alive, stopped for good once the sink seals or the bearer is
|
|
341
|
+
// refused. `tokensDirty` keeps a quiet tick (or a dropped snapshot with no
|
|
342
|
+
// newer text) from posting again.
|
|
343
|
+
let flushTimer: ReturnType<typeof setInterval> | null = null
|
|
344
|
+
let flushTimerStopped = false
|
|
345
|
+
let tokensDirty = false
|
|
346
|
+
let timerFlushQueued = false
|
|
347
|
+
function stopFlushTimer(): void {
|
|
348
|
+
flushTimerStopped = true
|
|
349
|
+
if (flushTimer) clearInterval(flushTimer)
|
|
350
|
+
flushTimer = null
|
|
351
|
+
}
|
|
352
|
+
function onFlushTick(): void {
|
|
353
|
+
if (sealed) return stopFlushTimer()
|
|
354
|
+
if (!tokensDirty || timerFlushQueued) return
|
|
355
|
+
timerFlushQueued = true
|
|
356
|
+
serialize(async () => {
|
|
357
|
+
timerFlushQueued = false
|
|
358
|
+
// Queued behind finish(), the sink may have sealed since the tick fired.
|
|
359
|
+
if (sealed) return
|
|
360
|
+
tokensDirty = false
|
|
361
|
+
await flushTokens()
|
|
362
|
+
}).catch(error => {
|
|
363
|
+
// A refused bearer would be refused again every tick; the loop learns of
|
|
364
|
+
// it at its next structural event or finish.
|
|
365
|
+
if (error instanceof RunAuthError) stopFlushTimer()
|
|
366
|
+
diag(`timer token flush failed: ${error instanceof Error ? error.message : String(error)}`)
|
|
367
|
+
})
|
|
368
|
+
}
|
|
315
369
|
|
|
316
370
|
function assertWritable(): void {
|
|
317
371
|
if (sealed) {
|
|
@@ -389,6 +443,7 @@ export function createRunEventSink(options: CreateRunEventSinkOptions): RunEvent
|
|
|
389
443
|
// Run gone/terminal server-side: seal and stop (Orizu records win).
|
|
390
444
|
if (response.status === 404 || response.status === 410) {
|
|
391
445
|
sealed = true
|
|
446
|
+
terminatedServerSide = true
|
|
392
447
|
diag(`append rejected (${response.status}) for ${eventType} — sealing sink (run terminal)`)
|
|
393
448
|
throw new RunTerminalError(`append rejected (${response.status}) — run is terminal server-side`)
|
|
394
449
|
}
|
|
@@ -434,10 +489,10 @@ export function createRunEventSink(options: CreateRunEventSinkOptions): RunEvent
|
|
|
434
489
|
// REENTRANCY CONTRACT: flushTokens MUST run to completion before the next
|
|
435
490
|
// append()/flushTokens() begins — it is NOT safe to run concurrently with a
|
|
436
491
|
// token append. Coalescing correctness (and the `===` re-check below) relies on
|
|
437
|
-
//
|
|
438
|
-
//
|
|
439
|
-
//
|
|
440
|
-
//
|
|
492
|
+
// every append/flush running strictly sequentially, so no newer delta can
|
|
493
|
+
// mutate `tokenBuffer` mid-flush. The public methods and the ORI-2340 timer
|
|
494
|
+
// flush all go through `serialize` to keep that true; any new caller of this
|
|
495
|
+
// inner function MUST too, or the drop-preserving guarantee breaks.
|
|
441
496
|
async function flushTokens(): Promise<void> {
|
|
442
497
|
if (tokenBuffer.size === 0) return
|
|
443
498
|
// Snapshot the pending entries but DO NOT clear the buffer up front: only drop
|
|
@@ -467,6 +522,11 @@ export function createRunEventSink(options: CreateRunEventSinkOptions): RunEvent
|
|
|
467
522
|
const messageId = event.messageId ?? '_'
|
|
468
523
|
const text = typeof event.payload.text === 'string' ? event.payload.text : ''
|
|
469
524
|
tokenBuffer.set(messageId, text)
|
|
525
|
+
tokensDirty = true
|
|
526
|
+
if (!flushTimer && !flushTimerStopped) {
|
|
527
|
+
flushTimer = setInterval(onFlushTick, options.tokenFlushMs ?? TOKEN_FLUSH_MS)
|
|
528
|
+
flushTimer.unref?.()
|
|
529
|
+
}
|
|
470
530
|
return
|
|
471
531
|
}
|
|
472
532
|
const eventType = eventTypeForKind(event.kind)
|
|
@@ -586,12 +646,15 @@ export function createRunEventSink(options: CreateRunEventSinkOptions): RunEvent
|
|
|
586
646
|
}
|
|
587
647
|
|
|
588
648
|
return {
|
|
589
|
-
append,
|
|
590
|
-
flushTokens,
|
|
591
|
-
finish,
|
|
649
|
+
append: event => serialize(() => append(event)),
|
|
650
|
+
flushTokens: () => serialize(flushTokens),
|
|
651
|
+
finish: (status, opts) => serialize(() => finish(status, opts)),
|
|
592
652
|
get sealed() {
|
|
593
653
|
return sealed
|
|
594
654
|
},
|
|
655
|
+
get terminatedServerSide() {
|
|
656
|
+
return terminatedServerSide
|
|
657
|
+
},
|
|
595
658
|
get nextSequence() {
|
|
596
659
|
return nextSequence
|
|
597
660
|
},
|
|
@@ -737,7 +800,9 @@ export async function drainHarnessToSink(
|
|
|
737
800
|
: finishOpts
|
|
738
801
|
try {
|
|
739
802
|
await sink.finish(status, mergedFinishOpts)
|
|
740
|
-
|
|
803
|
+
// A timer flush queued ahead of finish() can meet the server's 404/410
|
|
804
|
+
// after the check above; finish() then no-ops, so honour the server.
|
|
805
|
+
return sink.terminatedServerSide ? 'cancelled' : status
|
|
741
806
|
} catch (error) {
|
|
742
807
|
if (error instanceof RunTerminalError) return 'cancelled'
|
|
743
808
|
throw error
|
|
@@ -272,6 +272,8 @@ orizu apps archive <app-id> --project my-team/quality-eval
|
|
|
272
272
|
orizu apps restore <app-id> --project my-team/quality-eval
|
|
273
273
|
```
|
|
274
274
|
|
|
275
|
+
An app's page link is `<server>/preview/<app-id>` (`https://app.orizu.ai` by default). `apps list` prints a `Link: <app> <link>` line under the table for each app that is not archived; `apps list --json` has `pageUrl` for each app. `apps create` and `apps update` print it after `View app:`; `apps create --json` and `apps update --json` both have it as `app.pageUrl`. Get `<team>/<project>` from `orizu projects list`. Give that link; never guess one.
|
|
276
|
+
|
|
275
277
|
Create from file:
|
|
276
278
|
|
|
277
279
|
```bash
|