orizu 0.5.0 → 0.5.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.
@@ -54,18 +54,29 @@ export async function runGithubLink(teamSlug, io) {
54
54
  const { installUrl, state } = (await start.json());
55
55
  io.print(' Connect your team GitHub org by installing the Orizu GitHub App:');
56
56
  io.print(` ${installUrl}`);
57
+ // Bug 2: personal accounts cannot host an auto-provisioned workbench.
58
+ io.print(' • Install into a GitHub ORGANIZATION, not a personal account. Personal');
59
+ io.print(' accounts are not supported for auto-provisioning; creating an org is free.');
60
+ // Bug 4: repo selection guidance — GitHub forces picking at least one repo.
61
+ io.print(' • On the "Repository access" step choose "Only select repositories" and pick');
62
+ io.print(' any one repo (GitHub requires at least one). Orizu adds the workbench repo');
63
+ io.print(' it creates automatically — do NOT choose "All repositories".');
57
64
  if (io.openUrl) {
58
65
  io.openUrl(installUrl);
59
- io.print(' Opened the install page in your browser. Waiting for you to finish...');
66
+ io.print(' Opened the install page in your browser.');
60
67
  }
61
68
  else {
62
- io.print(' Open the URL above, install the App, then return here. Waiting...');
69
+ io.print(' Open the URL above and install the App, then return here.');
63
70
  }
71
+ io.print(` Waiting for you to finish installing in the browser… (Ctrl+C to abort; resume later with \`orizu github link --team ${teamSlug}\`).`);
64
72
  const now = io.now ?? (() => Date.now());
65
73
  const sleep = io.sleep ?? defaultSleep;
66
74
  const interval = io.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
67
75
  const timeout = io.pollTimeoutMs ?? DEFAULT_POLL_TIMEOUT_MS;
68
76
  const deadline = now() + timeout;
77
+ // Periodic heartbeat so a long install does not look hung; never echoes state.
78
+ const HEARTBEAT_MS = 15000;
79
+ let lastHeartbeat = now();
69
80
  for (;;) {
70
81
  const poll = await fetcher(`/api/cli/github/link?state=${encodeURIComponent(state)}`);
71
82
  if (poll.ok) {
@@ -77,11 +88,22 @@ export async function runGithubLink(teamSlug, io) {
77
88
  if (data.status === 'pending_confirmation') {
78
89
  return await confirmLink(teamSlug, data.orgLogin ?? null, io, fetcher);
79
90
  }
91
+ // Distinguishable terminal errors (personal account / expired nonce): print
92
+ // the server's reason and exit non-zero instead of polling until timeout.
93
+ if (data.status === 'unsupported_account' || data.status === 'expired') {
94
+ throw new Error(data.error ??
95
+ `GitHub link could not be completed. Re-run \`orizu github link --team ${teamSlug}\`.`);
96
+ }
97
+ // status 'pending' → keep waiting.
80
98
  }
81
99
  if (now() >= deadline) {
82
100
  throw new Error('Timed out waiting for the GitHub App install to complete. ' +
83
101
  `Resume with \`orizu github link --team ${teamSlug}\` after finishing the install.`);
84
102
  }
103
+ if (now() - lastHeartbeat >= HEARTBEAT_MS) {
104
+ io.print(' …still waiting for the GitHub install to complete.');
105
+ lastHeartbeat = now();
106
+ }
85
107
  await sleep(interval);
86
108
  }
87
109
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orizu",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {
@@ -86,23 +86,40 @@ export async function runGithubLink(teamSlug: string, io: GithubLinkIo): Promise
86
86
 
87
87
  io.print(' Connect your team GitHub org by installing the Orizu GitHub App:')
88
88
  io.print(` ${installUrl}`)
89
+ // Bug 2: personal accounts cannot host an auto-provisioned workbench.
90
+ io.print(' • Install into a GitHub ORGANIZATION, not a personal account. Personal')
91
+ io.print(' accounts are not supported for auto-provisioning; creating an org is free.')
92
+ // Bug 4: repo selection guidance — GitHub forces picking at least one repo.
93
+ io.print(' • On the "Repository access" step choose "Only select repositories" and pick')
94
+ io.print(' any one repo (GitHub requires at least one). Orizu adds the workbench repo')
95
+ io.print(' it creates automatically — do NOT choose "All repositories".')
89
96
  if (io.openUrl) {
90
97
  io.openUrl(installUrl)
91
- io.print(' Opened the install page in your browser. Waiting for you to finish...')
98
+ io.print(' Opened the install page in your browser.')
92
99
  } else {
93
- io.print(' Open the URL above, install the App, then return here. Waiting...')
100
+ io.print(' Open the URL above and install the App, then return here.')
94
101
  }
102
+ io.print(
103
+ ` Waiting for you to finish installing in the browser… (Ctrl+C to abort; resume later with \`orizu github link --team ${teamSlug}\`).`
104
+ )
95
105
 
96
106
  const now = io.now ?? (() => Date.now())
97
107
  const sleep = io.sleep ?? defaultSleep
98
108
  const interval = io.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS
99
109
  const timeout = io.pollTimeoutMs ?? DEFAULT_POLL_TIMEOUT_MS
100
110
  const deadline = now() + timeout
111
+ // Periodic heartbeat so a long install does not look hung; never echoes state.
112
+ const HEARTBEAT_MS = 15000
113
+ let lastHeartbeat = now()
101
114
 
102
115
  for (;;) {
103
116
  const poll = await fetcher(`/api/cli/github/link?state=${encodeURIComponent(state)}`)
104
117
  if (poll.ok) {
105
- const data = (await poll.json()) as { status: string; orgLogin?: string | null }
118
+ const data = (await poll.json()) as {
119
+ status: string
120
+ orgLogin?: string | null
121
+ error?: string
122
+ }
106
123
  if (data.status === 'active') {
107
124
  io.print(` GitHub org connected${data.orgLogin ? ` (${data.orgLogin})` : ''}.`)
108
125
  return { status: 'active', orgLogin: data.orgLogin ?? null, alreadyLinked: false }
@@ -110,6 +127,15 @@ export async function runGithubLink(teamSlug: string, io: GithubLinkIo): Promise
110
127
  if (data.status === 'pending_confirmation') {
111
128
  return await confirmLink(teamSlug, data.orgLogin ?? null, io, fetcher)
112
129
  }
130
+ // Distinguishable terminal errors (personal account / expired nonce): print
131
+ // the server's reason and exit non-zero instead of polling until timeout.
132
+ if (data.status === 'unsupported_account' || data.status === 'expired') {
133
+ throw new Error(
134
+ data.error ??
135
+ `GitHub link could not be completed. Re-run \`orizu github link --team ${teamSlug}\`.`
136
+ )
137
+ }
138
+ // status 'pending' → keep waiting.
113
139
  }
114
140
  if (now() >= deadline) {
115
141
  throw new Error(
@@ -117,6 +143,10 @@ export async function runGithubLink(teamSlug: string, io: GithubLinkIo): Promise
117
143
  `Resume with \`orizu github link --team ${teamSlug}\` after finishing the install.`
118
144
  )
119
145
  }
146
+ if (now() - lastHeartbeat >= HEARTBEAT_MS) {
147
+ io.print(' …still waiting for the GitHub install to complete.')
148
+ lastHeartbeat = now()
149
+ }
120
150
  await sleep(interval)
121
151
  }
122
152
  }