@nurix/nustack 0.10.0-dev.12 → 0.10.0-dev.14

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.
Files changed (52) hide show
  1. package/dist/commands/bootstrap.js +4 -4
  2. package/dist/commands/docs.js +13 -13
  3. package/dist/commands/doctor.js +13 -13
  4. package/dist/commands/handoff.js +8 -8
  5. package/dist/commands/init.js +43 -43
  6. package/dist/commands/{workspace-sync.js → project-sync.js} +5 -5
  7. package/dist/commands/{workspace.js → project.js} +133 -133
  8. package/dist/commands/review.js +5 -5
  9. package/dist/commands/search.js +7 -7
  10. package/dist/commands/sessions.js +1 -1
  11. package/dist/commands/sync.js +10 -10
  12. package/dist/commands/toolkit.js +19 -19
  13. package/dist/index.js +29 -29
  14. package/dist/lib/etna/profile.js +9 -9
  15. package/dist/lib/etna/reconcile.js +11 -11
  16. package/dist/lib/git-sync/core/engine.js +4 -4
  17. package/dist/lib/git-sync/inbound/apply_commit.js +3 -3
  18. package/dist/lib/git-sync/inbound/approval.js +1 -1
  19. package/dist/lib/git-sync/inbound/download.js +8 -8
  20. package/dist/lib/git-sync/inbound/event_sync.js +13 -13
  21. package/dist/lib/git-sync/inbound/notification.js +1 -1
  22. package/dist/lib/git-sync/outbound/extraction.js +4 -4
  23. package/dist/lib/git-sync/outbound/history_decision.js +2 -2
  24. package/dist/lib/git-sync/outbound/publisher.js +2 -2
  25. package/dist/lib/git-sync/outbound/queue.js +3 -3
  26. package/dist/lib/git-sync/outbound/upload_client.js +4 -4
  27. package/dist/lib/git-sync/whitelist/index.js +1 -1
  28. package/dist/lib/git-sync/whitelist/types.js +1 -1
  29. package/dist/lib/git-sync/whitelist/validation.js +5 -5
  30. package/dist/lib/lanes/supervisor.js +8 -8
  31. package/dist/lib/local_store/accepted_uploads.js +5 -5
  32. package/dist/lib/local_store/bindings.js +11 -11
  33. package/dist/lib/local_store/index.js +1 -1
  34. package/dist/lib/local_store/migrations.js +13 -0
  35. package/dist/lib/local_store/project_events_cache.js +54 -0
  36. package/dist/lib/nustack_client.js +45 -45
  37. package/dist/lib/onboarding/attach_existing_git.js +7 -7
  38. package/dist/lib/onboarding/binding.js +11 -11
  39. package/dist/lib/onboarding/folder_state.js +10 -10
  40. package/dist/lib/onboarding/handoff_apply.js +2 -2
  41. package/dist/lib/onboarding/init_fresh_folder.js +18 -18
  42. package/dist/lib/onboarding/manifest_writer.js +31 -31
  43. package/dist/lib/onboarding/web_first_attach.js +12 -12
  44. package/dist/lib/{workspace_context.js → project_context.js} +24 -24
  45. package/dist/lib/review/client.js +1 -1
  46. package/dist/lib/session_telemetry.js +2 -2
  47. package/dist/lib/sessions/client.js +8 -8
  48. package/dist/lib/sessions/outsideSessions.js +3 -3
  49. package/dist/lib/telemetry/config_renderer.js +3 -3
  50. package/dist/lib/telemetry/health_report.js +1 -1
  51. package/package.json +3 -3
  52. package/dist/lib/local_store/workspace_events_cache.js +0 -54
@@ -1,10 +1,10 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import path from "node:path";
3
3
  import * as p from "@clack/prompts";
4
- import { createWorkspaceViaAsk, getWorkspace, listWorkspaces, listOrganizations, publishWorkspace, resolveNuStackUrl, unpublishWorkspace, } from "../lib/nustack_client.js";
4
+ import { createProjectViaAsk, getProject, listProjects, listOrganizations, publishProject, resolveNuStackUrl, unpublishProject, } from "../lib/nustack_client.js";
5
5
  import { ensureAccessToken } from "../lib/auth_grant.js";
6
6
  import { ensureAuthWithRecovery, lastAuthFailure } from "../lib/auth_recovery.js";
7
- import { resolveWorkspaceId, resolveOrganizationId } from "../lib/workspace_context.js";
7
+ import { resolveProjectId, resolveOrganizationId } from "../lib/project_context.js";
8
8
  import { readActiveOrganizationContext } from "./organization.js";
9
9
  import { confirmOrFallback, isInteractive } from "../lib/interactive.js";
10
10
  import { launchBrowser } from "../lib/browser_opener.js";
@@ -13,7 +13,7 @@ import { openLocalStore } from "../lib/local_store/database.js";
13
13
  import { readDeviceState } from "../lib/local_store/device_state.js";
14
14
  import { getBindingByRepositoryRoot, listBindings } from "../lib/local_store/bindings.js";
15
15
  import { resolveRepository } from "../lib/git-sync/core/repository.js";
16
- import { readWorkspaceManifests } from "../lib/onboarding/manifest_writer.js";
16
+ import { readProjectManifests } from "../lib/onboarding/manifest_writer.js";
17
17
  import { hasManifest, scaffoldManifest } from "../lib/manifest.js";
18
18
  import { toolkitCompatibility } from "../lib/etna/reconcile.js";
19
19
  import { attachExistingGit } from "../lib/onboarding/attach_existing_git.js";
@@ -37,96 +37,96 @@ async function requireAuth(command, json, globals) {
37
37
  }
38
38
  return { baseUrl, accessToken: deviceAuth.accessToken };
39
39
  }
40
- async function resolveOrganizationForWorkspace(auth, options) {
40
+ async function resolveOrganizationForProject(auth, options) {
41
41
  const memberships = await listOrganizations(auth.baseUrl, auth.accessToken);
42
42
  const views = memberships.map((m) => ({ id: m.organization.id, slug: m.organization.slug }));
43
43
  const active = await readActiveOrganizationContext();
44
44
  const resolution = resolveOrganizationId({ organization: options.organization }, active, views);
45
45
  return resolution.ok ? { ok: true, organizationId: resolution.organizationId } : { ok: false, message: resolution.message };
46
46
  }
47
- export async function runWorkspaceList(options, globals) {
47
+ export async function runProjectList(options, globals) {
48
48
  const json = options.json === true;
49
- const auth = await requireAuth("workspace list", json, globals);
49
+ const auth = await requireAuth("project list", json, globals);
50
50
  if (!auth)
51
51
  return;
52
- const ws = await resolveOrganizationForWorkspace(auth, options);
52
+ const ws = await resolveOrganizationForProject(auth, options);
53
53
  if (!ws.ok) {
54
- emitFail("workspace list", json, "ORGANIZATION_CONTEXT_MISSING", ws.message);
54
+ emitFail("project list", json, "ORGANIZATION_CONTEXT_MISSING", ws.message);
55
55
  return;
56
56
  }
57
- const workspaces = await listWorkspaces(auth.baseUrl, auth.accessToken, ws.organizationId);
57
+ const projects = await listProjects(auth.baseUrl, auth.accessToken, ws.organizationId);
58
58
  if (json) {
59
- printJsonSuccess("workspace list", { organizationId: ws.organizationId, workspaces });
59
+ printJsonSuccess("project list", { organizationId: ws.organizationId, projects });
60
60
  return;
61
61
  }
62
- if (workspaces.length === 0) {
63
- console.log("No Workspaces in this Organization. Create one with `nustack workspace create`.");
62
+ if (projects.length === 0) {
63
+ console.log("No Projects in this Organization. Create one with `nustack project create`.");
64
64
  return;
65
65
  }
66
- for (const workspace of workspaces) {
67
- console.log(`${workspace.id.padEnd(24)} ${workspace.status.padEnd(12)} ${workspace.publicationState.padEnd(12)} ${workspace.label}`);
66
+ for (const project of projects) {
67
+ console.log(`${project.id.padEnd(24)} ${project.status.padEnd(12)} ${project.publicationState.padEnd(12)} ${project.label}`);
68
68
  }
69
69
  }
70
- export async function runWorkspaceCreate(options, globals) {
70
+ export async function runProjectCreate(options, globals) {
71
71
  const json = options.json === true;
72
- const auth = await requireAuth("workspace create", json, globals);
72
+ const auth = await requireAuth("project create", json, globals);
73
73
  if (!auth)
74
74
  return;
75
- const ws = await resolveOrganizationForWorkspace(auth, options);
75
+ const ws = await resolveOrganizationForProject(auth, options);
76
76
  if (!ws.ok) {
77
- emitFail("workspace create", json, "ORGANIZATION_CONTEXT_MISSING", ws.message);
77
+ emitFail("project create", json, "ORGANIZATION_CONTEXT_MISSING", ws.message);
78
78
  return;
79
79
  }
80
80
  const gathered = await gatherCreatePrompt(options, json);
81
81
  if (!gathered.ok) {
82
- emitFail("workspace create", json, "VALIDATION_FAILED", gathered.message);
82
+ emitFail("project create", json, "VALIDATION_FAILED", gathered.message);
83
83
  return;
84
84
  }
85
- const workspace = await createWorkspaceFromPrompt(auth, ws.organizationId, gathered.prompt, "workspace create", json);
86
- if (!workspace)
85
+ const project = await createProjectFromPrompt(auth, ws.organizationId, gathered.prompt, "project create", json);
86
+ if (!project)
87
87
  return;
88
88
  if (json) {
89
- printJsonSuccess("workspace create", { workspace, organizationId: ws.organizationId });
89
+ printJsonSuccess("project create", { project, organizationId: ws.organizationId });
90
90
  return;
91
91
  }
92
- console.log(`Created Workspace ${workspace.label} (${workspace.id}), status ${workspace.status}.`);
93
- console.log(`Attach a local folder with \`nustack workspace attach --workspace ${workspace.id}\`.`);
94
- if (isInteractive() && (await confirmOrFallback("Attach the current folder to this Workspace now?", false))) {
92
+ console.log(`Created Project ${project.label} (${project.id}), status ${project.status}.`);
93
+ console.log(`Attach a local folder with \`nustack project attach --project ${project.id}\`.`);
94
+ if (isInteractive() && (await confirmOrFallback("Attach the current folder to this Project now?", false))) {
95
95
  const store = await openLocalStore();
96
96
  try {
97
97
  const ctx = buildOnboardingContext(auth, store, json, options.yes === true);
98
98
  const outcome = await webFirstAttach(ctx, {
99
- workspaceId: workspace.id,
99
+ projectId: project.id,
100
100
  organizationId: ws.organizationId,
101
101
  confirmPopulated: options.yes === true,
102
102
  });
103
- reportWebFirst("workspace create", outcome, json);
103
+ reportWebFirst("project create", outcome, json);
104
104
  }
105
105
  finally {
106
106
  store.close();
107
107
  }
108
108
  }
109
109
  }
110
- export async function runWorkspaceAttach(options, globals) {
110
+ export async function runProjectAttach(options, globals) {
111
111
  const json = options.json === true;
112
- const auth = await requireAuth("workspace attach", json, globals);
112
+ const auth = await requireAuth("project attach", json, globals);
113
113
  if (!auth)
114
114
  return;
115
115
  const store = await openLocalStore();
116
116
  try {
117
117
  const ctx = buildOnboardingContext(auth, store, json, options.yes === true);
118
- if (options.workspace) {
119
- const ws = await resolveOrganizationForWorkspace(auth, options);
118
+ if (options.project) {
119
+ const ws = await resolveOrganizationForProject(auth, options);
120
120
  if (!ws.ok) {
121
- emitFail("workspace attach", json, "ORGANIZATION_CONTEXT_MISSING", ws.message);
121
+ emitFail("project attach", json, "ORGANIZATION_CONTEXT_MISSING", ws.message);
122
122
  return;
123
123
  }
124
124
  const outcome = await webFirstAttach(ctx, {
125
- workspaceId: options.workspace,
125
+ projectId: options.project,
126
126
  organizationId: ws.organizationId,
127
127
  confirmPopulated: options.yes === true,
128
128
  });
129
- reportWebFirst("workspace attach", outcome, json);
129
+ reportWebFirst("project attach", outcome, json);
130
130
  return;
131
131
  }
132
132
  let root;
@@ -134,31 +134,31 @@ export async function runWorkspaceAttach(options, globals) {
134
134
  root = resolveRepository(ctx.cwd).repositoryRoot;
135
135
  }
136
136
  catch {
137
- emitFail("workspace attach", json, "NOT_A_WORKTREE", "`nustack workspace attach` needs a Git worktree — run `nustack init` to establish one, or pass `--workspace <id>` for a web-created Workspace.");
137
+ emitFail("project attach", json, "NOT_A_WORKTREE", "`nustack project attach` needs a Git worktree — run `nustack init` to establish one, or pass `--project <id>` for a web-created Project.");
138
138
  return;
139
139
  }
140
- const resolved = await resolveOrCreateWorkspaceForAttach(auth, options, root, json);
140
+ const resolved = await resolveOrCreateProjectForAttach(auth, options, root, json);
141
141
  if (!resolved.ok)
142
142
  return;
143
143
  if (!hasManifest(root))
144
144
  await scaffoldManifest(root, deriveSlug(path.basename(root)));
145
145
  const outcome = await attachExistingGit(ctx, {
146
- workspaceId: resolved.workspaceId,
146
+ projectId: resolved.projectId,
147
147
  organizationId: resolved.organizationId,
148
148
  confirmRebind: options.yes === true,
149
149
  });
150
- reportAttach("workspace attach", outcome, json);
150
+ reportAttach("project attach", outcome, json);
151
151
  }
152
152
  finally {
153
153
  store.close();
154
154
  }
155
155
  }
156
- export async function runWorkspaceStatus(options, globals) {
156
+ export async function runProjectStatus(options, globals) {
157
157
  const json = options.json === true;
158
158
  const cwd = process.cwd();
159
- const workspace = await resolveWorkspaceId(cwd, { workspace: options.workspace });
160
- if (!workspace.ok) {
161
- emitFail("workspace status", json, "WORKSPACE_CONTEXT_MISSING", workspace.message);
159
+ const project = await resolveProjectId(cwd, { project: options.project });
160
+ if (!project.ok) {
161
+ emitFail("project status", json, "PROJECT_CONTEXT_MISSING", project.message);
162
162
  return;
163
163
  }
164
164
  const baseUrl = resolveNuStackUrl(globals);
@@ -166,44 +166,44 @@ export async function runWorkspaceStatus(options, globals) {
166
166
  const store = await openLocalStore();
167
167
  let binding = null;
168
168
  try {
169
- binding = findBindingForWorkspace(store, workspace.workspaceId, cwd);
169
+ binding = findBindingForProject(store, project.projectId, cwd);
170
170
  }
171
171
  finally {
172
172
  store.close();
173
173
  }
174
- let cloudWorkspace = null;
174
+ let cloudProject = null;
175
175
  let cloud = null;
176
176
  if (auth) {
177
177
  try {
178
- cloudWorkspace = await getWorkspace(baseUrl, auth.accessToken, workspace.workspaceId);
179
- if (cloudWorkspace.toolkit && cloudWorkspace.toolkit.version && cloudWorkspace.toolkit.rendererVersion && cloudWorkspace.toolkit.placementMapVersion) {
178
+ cloudProject = await getProject(baseUrl, auth.accessToken, project.projectId);
179
+ if (cloudProject.toolkit && cloudProject.toolkit.version && cloudProject.toolkit.rendererVersion && cloudProject.toolkit.placementMapVersion) {
180
180
  cloud = {
181
- name: cloudWorkspace.toolkit.name,
182
- version: cloudWorkspace.toolkit.version,
183
- rendererVersion: cloudWorkspace.toolkit.rendererVersion,
184
- placementMapVersion: cloudWorkspace.toolkit.placementMapVersion,
181
+ name: cloudProject.toolkit.name,
182
+ version: cloudProject.toolkit.version,
183
+ rendererVersion: cloudProject.toolkit.rendererVersion,
184
+ placementMapVersion: cloudProject.toolkit.placementMapVersion,
185
185
  catalogVersionId: null,
186
186
  };
187
187
  }
188
188
  }
189
189
  catch {
190
- cloudWorkspace = null;
190
+ cloudProject = null;
191
191
  }
192
192
  }
193
193
  const compatibility = toolkitCompatibility(binding ?? { etnaProfileJson: null }, cloud);
194
194
  if (json) {
195
- printJsonSuccess("workspace status", {
196
- workspaceId: workspace.workspaceId,
197
- cloudReachable: cloudWorkspace !== null,
198
- cloud: cloudWorkspace
199
- ? { label: cloudWorkspace.label, status: cloudWorkspace.status, publicationState: cloudWorkspace.publicationState, initStatus: cloudWorkspace.initStatus }
195
+ printJsonSuccess("project status", {
196
+ projectId: project.projectId,
197
+ cloudReachable: cloudProject !== null,
198
+ cloud: cloudProject
199
+ ? { label: cloudProject.label, status: cloudProject.status, publicationState: cloudProject.publicationState, initStatus: cloudProject.initStatus }
200
200
  : null,
201
201
  binding: binding
202
202
  ? {
203
203
  id: binding.id,
204
204
  boundBranch: binding.boundBranch,
205
205
  lastProcessedCommitId: binding.lastProcessedCommitId,
206
- lastAppliedWorkspaceSequence: binding.lastAppliedWorkspaceSequence,
206
+ lastAppliedProjectSequence: binding.lastAppliedProjectSequence,
207
207
  status: binding.status,
208
208
  }
209
209
  : null,
@@ -211,71 +211,71 @@ export async function runWorkspaceStatus(options, globals) {
211
211
  });
212
212
  return;
213
213
  }
214
- console.log(`Workspace ${workspace.workspaceId}`);
215
- console.log(` cloud: ${cloudWorkspace ? `${cloudWorkspace.label} — ${cloudWorkspace.status} / ${cloudWorkspace.publicationState}` : "(unreachable — local-only view)"}`);
214
+ console.log(`Project ${project.projectId}`);
215
+ console.log(` cloud: ${cloudProject ? `${cloudProject.label} — ${cloudProject.status} / ${cloudProject.publicationState}` : "(unreachable — local-only view)"}`);
216
216
  if (binding) {
217
217
  console.log(` binding: ${binding.id} on ${binding.boundBranch} (${binding.status})`);
218
- console.log(` cursor: processed=${binding.lastProcessedCommitId?.slice(0, 12) ?? "—"} applied@${binding.lastAppliedWorkspaceSequence}`);
218
+ console.log(` cursor: processed=${binding.lastProcessedCommitId?.slice(0, 12) ?? "—"} applied@${binding.lastAppliedProjectSequence}`);
219
219
  }
220
220
  else {
221
221
  console.log(" binding: (this folder is not bound on this device)");
222
222
  }
223
223
  console.log(` toolkit: ${compatibility.blocksWrites ? `BLOCKED — ${compatibility.reason}` : "clear (projection and sync writes allowed)"}`);
224
224
  }
225
- export async function runWorkspaceOpen(options, globals) {
225
+ export async function runProjectOpen(options, globals) {
226
226
  const json = options.json === true;
227
- const workspace = await resolveWorkspaceId(process.cwd(), { workspace: options.workspace });
228
- if (!workspace.ok) {
229
- emitFail("workspace open", json, "WORKSPACE_CONTEXT_MISSING", workspace.message);
227
+ const project = await resolveProjectId(process.cwd(), { project: options.project });
228
+ if (!project.ok) {
229
+ emitFail("project open", json, "PROJECT_CONTEXT_MISSING", project.message);
230
230
  return;
231
231
  }
232
232
  const baseUrl = resolveNuStackUrl(globals);
233
233
  let url;
234
234
  try {
235
- url = assertSafeBrowserUrl(`${baseUrl}/workspaces/${encodeURIComponent(workspace.workspaceId)}`);
235
+ url = assertSafeBrowserUrl(`${baseUrl}/projects/${encodeURIComponent(project.projectId)}`);
236
236
  }
237
237
  catch (error) {
238
- emitFail("workspace open", json, "UNSAFE_URL", error instanceof Error ? error.message : String(error));
238
+ emitFail("project open", json, "UNSAFE_URL", error instanceof Error ? error.message : String(error));
239
239
  return;
240
240
  }
241
241
  if (json) {
242
- printJsonSuccess("workspace open", { workspaceId: workspace.workspaceId, url });
242
+ printJsonSuccess("project open", { projectId: project.projectId, url });
243
243
  return;
244
244
  }
245
245
  launchBrowser(url);
246
246
  console.log(`Opening ${url}`);
247
247
  }
248
- export async function runWorkspacePublish(options, globals) {
249
- return runPublication("publish", "workspace publish", options, globals);
248
+ export async function runProjectPublish(options, globals) {
249
+ return runPublication("publish", "project publish", options, globals);
250
250
  }
251
- export async function runWorkspaceUnpublish(options, globals) {
252
- return runPublication("unpublish", "workspace unpublish", options, globals);
251
+ export async function runProjectUnpublish(options, globals) {
252
+ return runPublication("unpublish", "project unpublish", options, globals);
253
253
  }
254
254
  async function runPublication(action, command, options, globals) {
255
255
  const json = options.json === true;
256
256
  const auth = await requireAuth(command, json, globals);
257
257
  if (!auth)
258
258
  return;
259
- const workspace = await resolveWorkspaceId(process.cwd(), { workspace: options.workspace });
260
- if (!workspace.ok) {
261
- emitFail(command, json, "WORKSPACE_CONTEXT_MISSING", workspace.message);
259
+ const project = await resolveProjectId(process.cwd(), { project: options.project });
260
+ if (!project.ok) {
261
+ emitFail(command, json, "PROJECT_CONTEXT_MISSING", project.message);
262
262
  return;
263
263
  }
264
264
  try {
265
265
  if (action === "publish")
266
- await publishWorkspace(auth.baseUrl, auth.accessToken, workspace.workspaceId, {}, randomUUID());
266
+ await publishProject(auth.baseUrl, auth.accessToken, project.projectId, {}, randomUUID());
267
267
  else
268
- await unpublishWorkspace(auth.baseUrl, auth.accessToken, workspace.workspaceId, randomUUID());
268
+ await unpublishProject(auth.baseUrl, auth.accessToken, project.projectId, randomUUID());
269
269
  }
270
270
  catch (error) {
271
271
  emitFail(command, json, action === "publish" ? "PUBLISH_FAILED" : "UNPUBLISH_FAILED", error instanceof Error ? error.message : String(error));
272
272
  return;
273
273
  }
274
274
  if (json) {
275
- printJsonSuccess(command, { workspaceId: workspace.workspaceId, publicationState: action === "publish" ? "public" : "private" });
275
+ printJsonSuccess(command, { projectId: project.projectId, publicationState: action === "publish" ? "public" : "private" });
276
276
  return;
277
277
  }
278
- console.log(action === "publish" ? `Published Workspace ${workspace.workspaceId}.` : `Unpublished Workspace ${workspace.workspaceId}.`);
278
+ console.log(action === "publish" ? `Published Project ${project.projectId}.` : `Unpublished Project ${project.projectId}.`);
279
279
  }
280
280
  function buildOnboardingContext(auth, store, json, yes) {
281
281
  const deviceState = readDeviceState(store);
@@ -297,61 +297,61 @@ async function gatherCreatePrompt(options, json) {
297
297
  return { ok: true, prompt: flagPrompt };
298
298
  const name = options.name?.trim();
299
299
  if (name)
300
- return { ok: true, prompt: `Create a workspace named "${name}".` };
300
+ return { ok: true, prompt: `Create a project named "${name}".` };
301
301
  if (json || !isInteractive()) {
302
302
  return { ok: false, message: "A prompt is required — pass `--prompt <text>` (or `--name <name>`)." };
303
303
  }
304
- const answer = await p.text({ message: "What should this workspace be?" });
304
+ const answer = await p.text({ message: "What should this project be?" });
305
305
  if (p.isCancel(answer) || !String(answer).trim())
306
306
  return { ok: false, message: "A prompt is required." };
307
307
  return { ok: true, prompt: String(answer).trim() };
308
308
  }
309
- async function createWorkspaceFromPrompt(auth, organizationId, prompt, command, json) {
309
+ async function createProjectFromPrompt(auth, organizationId, prompt, command, json) {
310
310
  let outcome;
311
311
  try {
312
- outcome = await createWorkspaceViaAsk(auth.baseUrl, auth.accessToken, organizationId, prompt, {
312
+ outcome = await createProjectViaAsk(auth.baseUrl, auth.accessToken, organizationId, prompt, {
313
313
  onPhase: json ? undefined : (message) => p.log.info(message),
314
314
  });
315
315
  }
316
316
  catch (error) {
317
- emitFail(command, json, "WORKSPACE_CREATE_FAILED", error instanceof Error ? error.message : String(error));
317
+ emitFail(command, json, "PROJECT_CREATE_FAILED", error instanceof Error ? error.message : String(error));
318
318
  return null;
319
319
  }
320
320
  if (outcome.generationError && !json) {
321
321
  p.log.warn(`The first document did not finish: ${outcome.generationError}`);
322
322
  }
323
- return getWorkspace(auth.baseUrl, auth.accessToken, outcome.workspaceId);
323
+ return getProject(auth.baseUrl, auth.accessToken, outcome.projectId);
324
324
  }
325
- async function resolveOrCreateWorkspaceForAttach(auth, options, root, json) {
326
- const marker = await resolveWorkspaceId(root, {});
325
+ async function resolveOrCreateProjectForAttach(auth, options, root, json) {
326
+ const marker = await resolveProjectId(root, {});
327
327
  if (marker.ok) {
328
- const manifests = await readWorkspaceManifests(root);
329
- const markerOrganization = manifests.workspaceManifest?.organizationId ?? manifests.serviceWorkspaceBinding?.organizationId ?? null;
328
+ const manifests = await readProjectManifests(root);
329
+ const markerOrganization = manifests.projectManifest?.organizationId ?? manifests.serviceProjectBinding?.organizationId ?? null;
330
330
  if (markerOrganization)
331
- return { ok: true, workspaceId: marker.workspaceId, organizationId: markerOrganization };
332
- const ws = await resolveOrganizationForWorkspace(auth, options);
331
+ return { ok: true, projectId: marker.projectId, organizationId: markerOrganization };
332
+ const ws = await resolveOrganizationForProject(auth, options);
333
333
  if (!ws.ok) {
334
- emitFail("workspace attach", json, "ORGANIZATION_CONTEXT_MISSING", ws.message);
334
+ emitFail("project attach", json, "ORGANIZATION_CONTEXT_MISSING", ws.message);
335
335
  return { ok: false };
336
336
  }
337
- return { ok: true, workspaceId: marker.workspaceId, organizationId: ws.organizationId };
337
+ return { ok: true, projectId: marker.projectId, organizationId: ws.organizationId };
338
338
  }
339
- const ws = await resolveOrganizationForWorkspace(auth, options);
339
+ const ws = await resolveOrganizationForProject(auth, options);
340
340
  if (!ws.ok) {
341
- emitFail("workspace attach", json, "ORGANIZATION_CONTEXT_MISSING", ws.message);
341
+ emitFail("project attach", json, "ORGANIZATION_CONTEXT_MISSING", ws.message);
342
342
  return { ok: false };
343
343
  }
344
344
  const gathered = await gatherCreatePrompt(options, json);
345
345
  if (!gathered.ok) {
346
- emitFail("workspace attach", json, "WORKSPACE_CONTEXT_MISSING", `No Workspace to attach — ${gathered.message} Or pass \`--workspace <id>\` for a web-created Workspace.`);
346
+ emitFail("project attach", json, "PROJECT_CONTEXT_MISSING", `No Project to attach — ${gathered.message} Or pass \`--project <id>\` for a web-created Project.`);
347
347
  return { ok: false };
348
348
  }
349
- const workspace = await createWorkspaceFromPrompt(auth, ws.organizationId, gathered.prompt, "workspace attach", json);
350
- if (!workspace)
349
+ const project = await createProjectFromPrompt(auth, ws.organizationId, gathered.prompt, "project attach", json);
350
+ if (!project)
351
351
  return { ok: false };
352
- return { ok: true, workspaceId: workspace.id, organizationId: ws.organizationId };
352
+ return { ok: true, projectId: project.id, organizationId: ws.organizationId };
353
353
  }
354
- function findBindingForWorkspace(store, workspaceId, cwd) {
354
+ function findBindingForProject(store, projectId, cwd) {
355
355
  try {
356
356
  const byRoot = getBindingByRepositoryRoot(store, resolveRepository(cwd).repositoryRoot);
357
357
  if (byRoot)
@@ -359,15 +359,15 @@ function findBindingForWorkspace(store, workspaceId, cwd) {
359
359
  }
360
360
  catch {
361
361
  }
362
- return listBindings(store).find((binding) => binding.workspaceId === workspaceId) ?? null;
362
+ return listBindings(store).find((binding) => binding.projectId === projectId) ?? null;
363
363
  }
364
364
  function deriveSlug(value) {
365
- return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "workspace";
365
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "project";
366
366
  }
367
367
  function toResultJson(result) {
368
368
  return {
369
369
  flow: result.flow,
370
- workspaceId: result.workspaceId,
370
+ projectId: result.projectId,
371
371
  organizationId: result.organizationId,
372
372
  bindingId: result.bindingId,
373
373
  boundBranch: result.boundBranch,
@@ -379,7 +379,7 @@ function toResultJson(result) {
379
379
  };
380
380
  }
381
381
  function printOnboardingHuman(result) {
382
- console.log(`Bound Workspace ${result.workspaceId} on branch ${result.boundBranch} (binding ${result.bindingId})${result.reused ? " [reused]" : ""}.`);
382
+ console.log(`Bound Project ${result.projectId} on branch ${result.boundBranch} (binding ${result.bindingId})${result.reused ? " [reused]" : ""}.`);
383
383
  const installNote = result.install.skipped
384
384
  ? result.install.reason ?? "no install"
385
385
  : result.install.installed
@@ -407,7 +407,7 @@ function reportWebFirst(command, outcome, json) {
407
407
  }
408
408
  printOnboardingHuman(result);
409
409
  if (result.overwriteRequired) {
410
- console.log(`Divergent target(s) — nothing was changed. Review, then run \`nustack workspace sync approve ${result.handoffId ?? ""}\`:`);
410
+ console.log(`Divergent target(s) — nothing was changed. Review, then run \`nustack project sync approve ${result.handoffId ?? ""}\`:`);
411
411
  for (const entry of result.divergent)
412
412
  console.log(` ${entry.path} (${entry.divergence})`);
413
413
  }
@@ -426,54 +426,54 @@ function reportAttach(command, outcome, json) {
426
426
  }
427
427
  printOnboardingHuman(outcome.result);
428
428
  }
429
- export function registerWorkspaceCommands(workspaceCommand, getGlobalOptions) {
430
- workspaceCommand
429
+ export function registerProjectCommands(projectCommand, getGlobalOptions) {
430
+ projectCommand
431
431
  .command("list")
432
- .description("List the Workspaces in the active (or --organization) Organization")
432
+ .description("List the Projects in the active (or --organization) Organization")
433
433
  .option("--organization <id>", "the Organization to list (defaults to the active Organization)")
434
434
  .option("--json", "emit the versioned machine envelope")
435
- .action(async (options) => runWorkspaceList(options, getGlobalOptions()));
436
- workspaceCommand
435
+ .action(async (options) => runProjectList(options, getGlobalOptions()));
436
+ projectCommand
437
437
  .command("create")
438
- .description("Create a Workspace from a prompt, then optionally attach the current folder")
438
+ .description("Create a Project from a prompt, then optionally attach the current folder")
439
439
  .option("--organization <id>", "the owning Organization (defaults to the active Organization)")
440
- .option("--prompt <text>", "what the Workspace should be — the first prompt creates it")
440
+ .option("--prompt <text>", "what the Project should be — the first prompt creates it")
441
441
  .option("--name <name>", "shorthand: builds the create prompt from a name")
442
442
  .option("--yes", "assume yes for onboarding confirmations")
443
443
  .option("--json", "emit the versioned machine envelope")
444
- .action(async (options) => runWorkspaceCreate(options, getGlobalOptions()));
445
- workspaceCommand
444
+ .action(async (options) => runProjectCreate(options, getGlobalOptions()));
445
+ projectCommand
446
446
  .command("attach")
447
- .description("Bind the current folder to a Workspace (existing-Git flow, or --workspace for a web-created Workspace)")
448
- .option("--workspace <id>", "attach to this web-created Workspace (web-first flow)")
447
+ .description("Bind the current folder to a Project (existing-Git flow, or --project for a web-created Project)")
448
+ .option("--project <id>", "attach to this web-created Project (web-first flow)")
449
449
  .option("--organization <id>", "the owning Organization (defaults to the active Organization)")
450
- .option("--prompt <text>", "the create prompt when creating a Workspace for this worktree")
450
+ .option("--prompt <text>", "the create prompt when creating a Project for this worktree")
451
451
  .option("--name <name>", "shorthand: builds the create prompt from a name")
452
452
  .option("--yes", "confirm populated-folder onboarding and rebind")
453
453
  .option("--json", "emit the versioned machine envelope")
454
- .action(async (options) => runWorkspaceAttach(options, getGlobalOptions()));
455
- workspaceCommand
454
+ .action(async (options) => runProjectAttach(options, getGlobalOptions()));
455
+ projectCommand
456
456
  .command("status")
457
- .description("Show the cloud Workspace summary merged with the local binding (degraded offline)")
458
- .option("--workspace <id>", "the Workspace to act as (defaults to the bound repository)")
457
+ .description("Show the cloud Project summary merged with the local binding (degraded offline)")
458
+ .option("--project <id>", "the Project to act as (defaults to the bound repository)")
459
459
  .option("--json", "emit the versioned machine envelope")
460
- .action(async (options) => runWorkspaceStatus(options, getGlobalOptions()));
461
- workspaceCommand
460
+ .action(async (options) => runProjectStatus(options, getGlobalOptions()));
461
+ projectCommand
462
462
  .command("open")
463
- .description("Open the Workspace in the browser (--json prints the URL instead of launching)")
464
- .option("--workspace <id>", "the Workspace to open (defaults to the bound repository)")
463
+ .description("Open the Project in the browser (--json prints the URL instead of launching)")
464
+ .option("--project <id>", "the Project to open (defaults to the bound repository)")
465
465
  .option("--json", "print the resolved URL as the machine envelope instead of launching")
466
- .action(async (options) => runWorkspaceOpen(options, getGlobalOptions()));
467
- workspaceCommand
466
+ .action(async (options) => runProjectOpen(options, getGlobalOptions()));
467
+ projectCommand
468
468
  .command("publish")
469
- .description("Publish the Workspace (role-gated)")
470
- .option("--workspace <id>", "the Workspace to publish (defaults to the bound repository)")
469
+ .description("Publish the Project (role-gated)")
470
+ .option("--project <id>", "the Project to publish (defaults to the bound repository)")
471
471
  .option("--json", "emit the versioned machine envelope")
472
- .action(async (options) => runWorkspacePublish(options, getGlobalOptions()));
473
- workspaceCommand
472
+ .action(async (options) => runProjectPublish(options, getGlobalOptions()));
473
+ projectCommand
474
474
  .command("unpublish")
475
- .description("Return the Workspace to private (role-gated)")
476
- .option("--workspace <id>", "the Workspace to unpublish (defaults to the bound repository)")
475
+ .description("Return the Project to private (role-gated)")
476
+ .option("--project <id>", "the Project to unpublish (defaults to the bound repository)")
477
477
  .option("--json", "emit the versioned machine envelope")
478
- .action(async (options) => runWorkspaceUnpublish(options, getGlobalOptions()));
478
+ .action(async (options) => runProjectUnpublish(options, getGlobalOptions()));
479
479
  }
@@ -117,13 +117,13 @@ export async function runReviewMirror(options, globals) {
117
117
  const answered = await client.answerReviewItem(id, verdict);
118
118
  return mirrorReport(json, "answered", answered.id);
119
119
  }
120
- const workspaceId = options.workspace?.trim() ?? "";
121
- if (!workspaceId)
122
- return mirrorReport(json, "skipped", null, "--workspace is required to open a mirrored prompt");
120
+ const projectId = options.project?.trim() ?? "";
121
+ if (!projectId)
122
+ return mirrorReport(json, "skipped", null, "--project is required to open a mirrored prompt");
123
123
  const subject = readSubject(options.subject);
124
124
  if (!subject)
125
125
  return mirrorReport(json, "skipped", null, "--subject must be a tool-permission JSON object");
126
- const created = await client.createReviewItem({ workspaceId, sessionId, subject });
126
+ const created = await client.createReviewItem({ projectId, sessionId, subject });
127
127
  return mirrorReport(json, "open", created.id);
128
128
  }
129
129
  catch (error) {
@@ -142,7 +142,7 @@ export function registerReviewCommands(reviewCommand, getGlobalOptions) {
142
142
  .command("mirror")
143
143
  .description("Reflect a lane's permission prompt, or its answer, into the Review pane")
144
144
  .option("--session <laneId>", "the lane's session id")
145
- .option("--workspace <id>", "the Workspace the lane's folder is bound to (required with --state open)")
145
+ .option("--project <id>", "the Project the lane's folder is bound to (required with --state open)")
146
146
  .option("--state <state>", "open or answered")
147
147
  .option("--subject <json>", "the tool-permission subject, as JSON (required with --state open)")
148
148
  .option("--verdict <verdict>", "allow or deny (required with --state answered)")
@@ -1,4 +1,4 @@
1
- import { resolveNuStackUrl, searchWorkspaces, searchCatalogue, } from "../lib/nustack_client.js";
1
+ import { resolveNuStackUrl, searchProjects, searchCatalogue, } from "../lib/nustack_client.js";
2
2
  export async function runSearch(keywords, options, globals) {
3
3
  const query = keywords.join(" ").trim();
4
4
  if (!query) {
@@ -12,20 +12,20 @@ export async function runSearch(keywords, options, globals) {
12
12
  return;
13
13
  }
14
14
  const baseUrl = resolveNuStackUrl(globals);
15
- const results = await searchWorkspaces(baseUrl, query, { category: options.category, limit: options.limit });
15
+ const results = await searchProjects(baseUrl, query, { category: options.category, limit: options.limit });
16
16
  if (options.json) {
17
17
  console.log(JSON.stringify(results, null, 2));
18
18
  return;
19
19
  }
20
20
  if (results.length === 0) {
21
- console.log(`No workspaces matched "${query}". Looking for a platform capability? Try \`nustack search services ${query}\`.`);
21
+ console.log(`No projects matched "${query}". Looking for a platform capability? Try \`nustack search services ${query}\`.`);
22
22
  return;
23
23
  }
24
24
  console.log(`${results.length} result${results.length === 1 ? "" : "s"} for "${query}"`);
25
- for (const workspace of results) {
26
- const category = workspace.categories[0] ?? "—";
27
- console.log(`${workspace.displayName.padEnd(28)} ${category.padEnd(16)} ${workspace.tagline ?? ""}`);
28
- const link = workspace.links[0];
25
+ for (const project of results) {
26
+ const category = project.categories[0] ?? "—";
27
+ console.log(`${project.displayName.padEnd(28)} ${category.padEnd(16)} ${project.tagline ?? ""}`);
28
+ const link = project.links[0];
29
29
  if (link)
30
30
  console.log(` ${link.url}`);
31
31
  }
@@ -25,7 +25,7 @@ function readTranscripts(value) {
25
25
  sessionKey,
26
26
  provider,
27
27
  modifiedMs,
28
- workspaceId: typeof row.workspaceId === "string" && row.workspaceId ? row.workspaceId : null,
28
+ projectId: typeof row.projectId === "string" && row.projectId ? row.projectId : null,
29
29
  vendorSessionId: typeof row.id === "string" && row.id ? row.id : null,
30
30
  ...(typeof row.sizeBytes === "number" ? { sizeBytes: row.sizeBytes } : {}),
31
31
  });